我们将研究每个泛型的计算过程
foldr
和
foldl
. 查看以下实现,比较如何
f
获取的结果
但是
福尔德尔
获取的结果
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 )
)
让我们仔细看看现在的过程
,你可以看到
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 )
的调用堆栈
福尔德尔
注意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
那么我们为什么要研究这些泛型呢?重点是向大家展示计算过程是什么样子的。在过程的可视化中,您可以看到数据如何在程序中移动。
在里面
福尔德
你可以看到
acc公司
立即向下传递到调用堆栈,因此您可以有效地删除
acc公司
参数和替换
acc公司
具有
0
这是你的
foldr0
const foldr0 = ( f , xs ) =>
isEmpty (xs)
? 0
: f ( foldr0 ( f , tail ( xs ) )
, head ( xs )
)
然而,事实并非如此
福尔德尔
一
新
acc在每个步骤中计算,需要计算
下一个
步骤,所以我们不能删除参数并替换为
0
就像我们对
. 相反,最简单(最智能)的实现变得
const foldl0 = ( f , xs ) =>
foldl ( f , 0 , xs )
const foldr0 = ( f , xs ) =>
foldr ( f , 0 , xs )
tl:dr;
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 )