我们将研究由每个泛型foldr
和foldl
. 看到下面的实现,比较如何f
得到的结果foldr
,但foldl
得到的结果f
const foldr = ( f , acc , xs ) =>
isEmpty (xs)
? acc
: f ( foldr ( f , acc , tail ( xs ) )
, head ( xs )
)
const foldl = ( f , acc , xs ) =>
isEmpty (xs)
? acc
: foldl ( f
, f ( acc , head ( xs ) )
, tail ( xs )
)
现在让我们仔细看看这个过程 - 在 中foldr
,您可以看到acc
( ) 在计算0
任何内容之前如何一直沿调用堆栈向下传递f
// example call
foldr ( f , 0 , [ 1 , 2 , 3 ] )
// notice we can't run f yet, still waiting for foldr
f ( foldr ( f , 0 , [ 2 , 3 ] )
, 1
)
// now 2 f calls pending; still waiting on foldr
f ( f ( foldr ( f , 0 , [ 3 ] )
, 2
)
, 1
)
// now 3 f calls pending, still waiting on foldr
f ( f ( f ( foldr ( f , 0 , [] )
, 3
)
, 2
)
, 1
)
// now we can compute the inner most f, working our way out
f ( f ( f ( 0
, 3
)
, 2
)
, 1
)
// in other words, foldr traverses your input and creates a stack of f calls
f ( f ( f ( 0 , 3 ) , 2 ) , 1 )
调用堆栈foldl
有很大不同——注意 acc 是如何立即使用的
// pretend f = ( x , y ) => x + y
foldl ( f
, 0
, [ 1 , 2 , 3 ]
)
// this time f is called first, and the result becomes the next acc
foldl ( f
, f ( 0 , 1 ) // next acc = 1
, [ 2 , 3 ]
)
// f is always computed before recurring
foldl ( f
, f ( 1 , 2 ) // next acc = 3
, [ 3 ]
)
// notice how foldl stays nice and flat (foldl uses a tail call, foldr doesn't)
foldl ( f
, f ( 3 , 3 ) // next acc = 6
, []
)
// when the input is empty, the acc is just returned
foldl ( f
, 6
, [] // empty input
)
// result
6
那么我们为什么要看这些泛型呢?那么重点是向您展示计算过程的样子。在流程的可视化中,您可以看到数据如何在程序中移动。
在foldr
您可以看到如何acc
立即从调用堆栈一路向下传递 – 因此您可以有效地删除acc
参数并替换acc
为0
您的foldr0
– 这正是您所拥有的
const foldr0 = ( f , xs ) =>
isEmpty (xs)
? 0
: f ( foldr0 ( f , tail ( xs ) )
, head ( xs )
)
然而,情况并非如此foldl
——每一步都会计算一个新的acc 并且需要计算下一步,因此我们不能0
像使用foldr
. 相反,最简单(也是最聪明)的实现变成
const foldl0 = ( f , xs ) =>
foldl ( f , 0 , xs )
const foldr0 = ( f , xs ) =>
foldr ( f , 0 , xs )
这不符合您“无辅助方法”的标准,但这并不是真的。该练习可能旨在向您展示为什么在没有辅助助手的情况下实现左折叠具有挑战性;作为帮助您可视化过程的一种手段。
tl:博士;
JavaScript 充满了各种各样的技巧,所以你可以作弊以通过你的课程并阻止自己学习任何东西!
const isEmpty = xs =>
xs.length === 0
const head = ( [ x , ... xs ] ) =>
x
const tail = ( [ x , ... xs ] ) =>
xs
const foldl0 = ( f , xs , acc = 0 ) =>
isEmpty (xs)
? acc
: foldl0 ( f
, tail ( xs )
, f ( acc , head ( xs ) )
)
const myFunc = ( x , y ) =>
`( ${x} + ${y} )`
console.log ( foldl0 ( myFunc , [ 1 , 2 , 3 ] ) )
// ( ( ( 0 + 1 ) + 2 ) + 3 )