簡體   English   中英

重組庫中的撰寫方法

[英]Compose method in recompose Library

我正在看@acdlite在recompose庫中的compose函數,以為高階組件構成邊界條件,這就是看起來像compose函數那樣

const compose = (...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg);

但是,我嘗試從https://medium.com/javascript-scene/reduce-composed-software-fe22f0c39a1d (特別是這段代碼)中使用Eric-Elliott的一種線性方法來編寫。

const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);

我嘗試在我的react組件中同時使用這兩種變體,

const ListWithConditionalRendering = compose(
  withLoadingIndicator,
  withDataNull,
  withListEmpty
)(Users);

他們似乎都工作正常。 我無法理解上述功能的工作方式是否有區別,如果有的話,它們有什么區別。

對於非常特殊的場景,存在一些差異可能會有所幫助。

第一個函數預組合了一個函數,這意味着它在組合時(而不是在調用時reduce()調用reduce() 相比之下,第二種方法返回一個范圍的函數調用reduceRight()調用時,而不是當它被組成。

第一個方法接受數組最后一個函數的多個參數,而第二個方法僅接受一個參數:

 const compose1 = (...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg); const compose2 = (...fns) => x => fns.reduceRight((v, f) => f(v), x); const f = s => (...args) => (console.log('function', s, 'length', args.length), args); compose1(f(1), f(2), f(3))(1, 2, 3); compose2(f(4), f(5), f(6))(1, 2, 3); 

如果函數數組由於預先組成而非常大,則第一種方法可能會導致堆棧溢出,而(相對) 第二種方法是堆棧安全的:

 const compose1 = (...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg); const compose2 = (...fns) => x => fns.reduceRight((v, f) => f(v), x); const f = v => v; try { compose1.apply(null, Array.from({ length: 1e5 }, () => f))(); console.log('1 is safe'); } catch (e) { console.log('1 failed'); } try { compose2.apply(null, Array.from({ length: 1e5 }, () => f))(); console.log('2 is safe'); } catch (e) { console.log('2 failed'); } 

†如果...fns太大,則第二種方法仍會導致堆棧溢出,因為arguments也在堆棧上分配。

如果您對reduce-composition實際構建的結構感興趣,則可以將其可視化如下:

 /* original: const compose = (...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg); */ const compose = (...funcs) => funcs.reduce((a, b) => `((...args) => ${a}(${b}(...args)))`, $_("id")); const $_ = name => `${name}`; const id = x => x; const inc = x => x + 1; const sqr = x => x * x; const neg = x => -x; const computation = compose($_("inc"), $_("sqr"), $_("neg")); console.log(computation); /* yields: ((...args) => ((...args) => ((...args) => id(inc(...args))) (sqr(...args))) (neg(...args))) */ console.log(eval(computation) (2)); // 5 (= id(inc(sqr(neg(2)))) 

那么這是怎么回事? 我用模板字符串替換了內部函數(...args) => a(b(...args)) ,並用$_ helper函數替換了arg => arg 然后,我將Template-String括在括號中,以使所得的String表示IIFE 最后但並非最不重要的一點是,我傳遞$_帶有正確名稱的$_ helper函數來compose

$_有點奇怪,但是可視化未應用/部分應用的函數確實很有幫助。

您可以從計算結構中看到,reduce-composition構建了一個匿名函數的嵌套結構,並且rest / spread操作分散在整個代碼中。

可視化和解釋部分應用的功能很困難。 我們可以通過省略內部匿名函數來簡化它:

 const compose = (...funcs) => funcs.reduce($xy("reducer"), $_("id")); const $_ = name => `${name}`; const $xy = name => (x, y) => `${name}(${x}, ${y})`; const id = x => x; const inc = x => x + 1; const sqr = x => x * x; const neg = x => -x; console.log( compose($_("inc"), $_("sqr"), $_("neg")) // reducer(reducer(reducer(id, inc), sqr), neg) ); 

我們可以通過實際運行合成來進一步簡化:

 const compose = (...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)), $x("id")); const $x = name => x => `${name}(${x})`; console.log( compose($x("inc"), $x("sqr"), $x("neg")) (2) // id(inc(sqr(neg(2)))) ); 

我相信像這樣的復雜計算的可視化是一種強大的技術,可以正確地理解它們並更好地理解嵌套/遞歸的計算結構。

實現顯示並告訴? 好的 -

 const identity = x => x const compose = (f = identity, ...fs) => x => f === identity ? x : compose (...fs) (f (x)) const add1 = x => x + 1 console .log ( compose () (0) // 0 , compose (add1) (0) // 1 , compose (add1, add1) (0) // 2 , compose (add1, add1, add1) (0) // 3 ) 

或者代替使用在線compose ...

const ListWithConditionalRendering = compose(
  withLoadingIndicator,
  withDataNull,
  withListEmpty
)(Users);

您可以在參數首先出現的地方進行某種“前向組合”功能-

 const $ = x => k => $ (k (x)) const add1 = x => x + 1 const double = x => x * 2 $ (0) (add1) (console.log) // 1 $ (2) (double) (double) (double) (console.log) // 16 $ (2) (double) (add1) (double) (console.log) // 10 

當您可以維持-的模式時, $很有用

$ (value) (pureFunc) (pureFunc) (pureFunc) (...) (effect)

上面, $將值放入某種“管道”中,但是無法將其取出 稍作調整,便可以編寫非常靈活的可變參數表達式。 下面,我們使用$來分隔流水線表達式的開始和結束。

 const $ = x => k => k === $ ? x : $ (k (x)) const double = x => x * 2 const a = $ (2) (double) ($) const b = $ (3) (double) (double) (double) ($) console .log (a, b) // 4 24 

這個可變的接口使您能夠編寫類似於在其他面向函數的語言中找到的令人垂涎的|>運算符的表達式-

value
  |> pureFunc
  |> pureFunc
  |> ...
  |> pureFunc

5 |> add1
  |> double
  |> double
  // 24

使用$ ,它轉換為-

$ (value) (pureFunc) (pureFunc) (...) (pureFunc) ($)

$ (5) (add1) (double) (double) ($) // 24

該技術還可以很好地與咖喱函數混合使用-

 const $ = x => k => $ (k (x)) const add = x => y => x + y const mult = x => y => x * y $ (1) (add (2)) (mult (3)) (console.log) // 9 

還是舉一個更有趣的例子-

 const $ = x => k => $ (k (x)) const flatMap = f => xs => xs .flatMap (f) const join = y => xs => xs .join (y) const twice = x => [ x, x ] $ ('mississippi') (([...chars]) => chars) (flatMap (twice)) (join ('')) (console.log) // 'mmiissssiissssiippppii' 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM