簡體   English   中英

通過ramda.js使用高階函數進行映射

[英]Mapping using higher-order functions with ramda.js

我在我的代碼,一再重復發生,似乎像它應該是相當常見的一種模式,但我不能為我的生活弄清楚它叫什么或是否有處理它常用方法: map使用函數ING是接受一個參數,該參數本身就是將map ed元素作為參數的函數的結果。

這是模式本身。 我已將要命名的函數命名為mapply (map-apply),但這似乎是錯誤的名稱:

const mapply = (outer, inner) => el => outer(inner(el))(el)

這到底叫什么? 如何在慣用的Ramda中實現它? 看起來好像世界上必須有聰明的人告訴我如何處理它。

我的用例是做一些基本的擬牛頓物理學工作,將力施加到對象上。 要計算一些力,您需要有關對象的一些信息-位置,質量,速度等。一個(非常)簡化的示例:

const g = Vector.create(0, 1),
    gravity = ({ mass }) => Vector.multiply(mass)(g),
    applyForce = force => body => {
        const { mass } = body, 
            acceleration = Vector.divide(mass)(force)

        return R.merge(body, { acceleration })
    }

//...

const gravitated = R.map(mapply(applyForce, gravity))(bodies)

有人可以告訴我:這是什么? 你會如何Ramda-fy? 我應該注意哪些陷阱,邊緣情況,困難? 有哪些明智的處理方式?

(我一直在搜索和搜索-SO,Ramda的GitHub存儲庫,其他一些功能編程資源。但是也許我的Google-fu並不需要它。如果我忽略了一些顯而易見的內容,我深表歉意。謝謝!)

這是一個組成。 它是專門compose (如果您想倒退,可以使用pipe )。

在數學中(考慮一下單變量演算),您將有諸如fxf(x)類的語句表示有某個函數f轉換x,並且該轉換將在其他地方進行描述。

然后,當您看到(g º f)(x)時,就會變得瘋狂。 “ G of F”(或許多其他描述)。

(g º f)(x) == g(f(x))

看起來熟悉?

const compose = (g, f) => x => g(f(x));

當然,您可以通過將組合函數用作組合函數內部的操作來擴展此范例。

const tripleAddOneAndHalve = compose(halve, compose(add1, triple));
tripleAddOneAndHalve(3); // 5

對於可變參數版本,您可以執行以下兩項操作之一,具體取決於您是想更深入地了解函數組成還是要理順一點。

// easier for most people to follow
const compose = (...fs) => x =>
  fs.reduceRight((x, f) => f(x), x);

// bakes many a noodle
const compose = (...fs) => x =>
  fs.reduceRight((f, g) => x => g(f(x)));

但是現在,如果您采用的是咖喱或局部map類的東西,例如:

const curry = (f, ...initialArgs) => (...additionalArgs) => {
  const arity = f.length;
  const args = [...initialArgs, ...additionalArgs];
  return args.length >= arity ? f(...args) : curry(f, ...args);
};

const map = curry((transform, functor) =>
  functor.map(transform));

const reduce = ((reducer, seed, reducible) =>
  reducible.reduce(reducer, seed));

const concat = (a, b) => a.concat(b);

const flatMap = curry((transform, arr) =>
  arr.map(transform).reduce(concat, []));

您可以做一些漂亮的事情:

const calculateCombinedAge = compose(
  reduce((total, age) => total + age, 0),
  map(employee => employee.age),
  flatMap(team => team.members));

const totalAge = calculateCombinedAge([{
  teamName: "A",
  members: [{ name: "Bob", age: 32 }, { name: "Sally", age: 20 }],
}, {
  teamName: "B",
  members: [{ name: "Doug", age: 35 }, { name: "Hannah", age: 41 }],
}]);  // 128

相當強大的東西。 當然,所有這些都可以在Ramda中獲得。

 const mapply0 = (outer, inner) => el => outer(inner(el))(el); const mapply1 = (outer, inner) => R.converge( R.uncurryN(2, outer), [ inner, R.identity, ], ); const mapply2 = R.useWith( R.converge, [ R.uncurry(2), R.prepend(R.__, [R.identity]), ], ); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script> 

我尚未對此進行測試,但它可能會起作用。

首先是您的功能。

第二種方法是使用converge將“ el”傳遞給內部函數,然后傳遞標識函數,然后將兩者傳遞給外部的未固化版本。

R.uncurryN(2,external)的工作方式與此外部(inner(el),el)相同,這意味着收斂可以提供參數。

第三個參數可能太遠了,但還是很有趣,您正在調用converge,其中第一個參數是外部的未固化版本,第二個參數是包含內部和標識的數組,useWith這樣做可以從解決方案中完全刪除函數定義。

我不確定這是否是您要尋找的東西,但這是我發現的三種書寫方式。

從對問題的評論中解釋:

mapply實際上是chain

R.chain(f, g)(x); //=> f(g(x), x)

好吧,主要是。 在這種情況下,請注意x必須是一個數組。

那么,我對這個問題的解決方案是:

const gravitated = R.map(
    R.chain(applyForce, R.compose(R.of, gravity))
)(bodies)

Ramda的鏈文檔在這種情況下並不是很有幫助:它讀起來很簡單,“鏈將函數映射到列表上並連接結果”。 (ramdajs.com/docs/#chain)

答案隱藏在第二個示例中,其中兩個函數傳遞給chain並部分應用。 在閱讀完這些答案后,我才能看到。

(感謝ftorbergiScott Sauyet 。)

暫無
暫無

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

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