【问题标题】:javascript functional / declarative version of for loop (with this example)?for 循环的 javascript 功能/声明性版本(使用此示例)?
【发布时间】:2019-09-22 15:39:39
【问题描述】:

有这个代码

const myMagic = (one, two, three, four) => `this is ${one} and ${two} and ${three} and ${four} as usual`

const txt = 'HELLO&ho&hy&hu&hq&HELLO&ho&hy&hu&hq&HELLO&ho&hy&hu&hq&HELLO&ho&hy&hu&hq&hx'
const fragments = txt.split('&')
const pieces = []

for (let i=0; i<fragments.length-1;i +=5) {
  pieces.push(fragments[i])
  pieces.push(myMagic(fragments[i+1],fragments[i+2],fragments[i+3],fragments[i+4]))
}

pieces.push(fragments[fragments.length-1])

console.log(pieces)

如何将其转换为更具声明性的版本?

代码就是这样,因为拆分采用了一个只解析一次文本的正则表达式,然后使用这些片段,我使用myMagic 函数构建了所需的组件

那么有什么方法可以在不改变逻辑的情况下以更具声明性的方式编写它?

【问题讨论】:

  • 定义“更具声明性”...?您需要以五个为一组进行循环。没有一个内置的数组函数可以做到这一点。您可以将其硬塞到其中之一中(也许),但这只会让您变得清晰恕我直言。
  • @T.J.Crowder 幸运的是,我们可以用 Javascript 编写自己的组合子,并采用 FP 中的通用组合子,然后组合/组合它们以允许声明性算法也用于给定任务。
  • @bob - 确实是,FP 或其他...

标签: javascript for-loop functional-programming declarative-programming


【解决方案1】:

对我来说,最佳点在于使用一些可以从 lodash、ramda 或任何其他稍微“功能性”的库中获得的实用程序,但将 [ a, f(b, c, d, e) ] 逻辑保留在常规箭头函数中。 (可能是个人喜好)

采取的步骤:

  • 将字符串拆分为一个字符串数组(我使用split("&amp;")
  • 将字符串数组拆分为包含 5 个字符串的数组 (chunk(5))
  • 在外部数组上调用flatMap
  • 使用([ head, ...tail]) =&gt; [ head, f(...tail) ] 映射内部数组,其中f 是您的“魔法”函数

// Utils
const range = s => Array.from(Array(Math.floor(s)), (_, i) => i);
const chunk = n => xs => range(xs.length / n)
  .map(i => xs.slice(i * n, i * n + n));
const split = del => str => str.split(del);
const flatMap = f => xs => xs.flatMap(f);
const pipe = (...fs) => x => fs.reduce((y, f) => f(y), x);

// App
const myMagic = (one, two, three, four) => `this is ${one} and ${two} and ${three} and ${four} as usual`

const convert = pipe(
  split("&"),
  chunk(5),
  flatMap(([ head, ...tail ]) => [ head, myMagic(...tail) ])
);

// Run it
const input = "HELLO1&ho&hy&hu&hq&HELLO2&ho&hy&hu&hq&HELLO3&ho&hy&hu&hq&HELLO4&ho&hy&hu&hq&hx";

console.log(convert(input));

【讨论】:

  • 不错,但我宁愿称它为 chunk,因为 ramda 的 aperture 是另一回事。
  • 啊,我明白了。 Ramda 的孔径使大小重叠的组n,而不是分裂(这是有道理的......)。我也刚刚注意到Bergi 也提到了chunk 这个名字:D
  • 我喜欢人们在 JS 中使用 monad!
  • 非常感谢您的详细解释,我个人仍然尽量避免使用外部库(我想尽快更正),但我看到你在这里做了什么,所以感谢你的时间:)尽管如此,将代码与 Bergi 的代码进行比较,您的代码长度几乎是两倍,这确实让我感到不安
  • 不客气!对我来说,“Utils”中的函数是我已经在我的项目中可用的函数,来自像 Ramda 这样的库或像这里这样自己编写的。因此,我不认为它们是“长度加倍”;)
【解决方案2】:

您总是可以使用递归函数来遍历列表:

const myMagic = (one, two, three, four) => `this is ${one} and ${two} and ${three} and ${four} as usual`

function pieces([zero, ...rest]) {
    if (!rest.length)
        return [zero];
    const [one, two, three, four, ...more] = rest;
    return [zero, myMagic(one, two, three, four), ...pieces(more)];
}

const txt = 'HELLO&ho&hy&hu&hq&HELLO&ho&hy&hu&hq&HELLO&ho&hy&hu&hq&HELLO&ho&hy&hu&hq&hx';
console.log(pieces(txt.split('&')))

我建议使用某种chunk(5) 函数并在其结果上使用flatMap

【讨论】:

  • 如果您已经返回 ...pieces(more),为什么我还需要 flat()?
  • @GWorking flat() 是什么意思?如果您指的是我的最后一段,请参阅 user3297291 的答案,了解我对 flatMap 的想法。
  • 是的,我明白你的意思,尽管我认为你在答案中的选择是最简单、更简洁和直观的,最后是需要考虑的。我正在使用您的解决方案,非常感谢!
【解决方案3】:

如果你喜欢声明式/函数式风格,何不试试ramda.js

let txt = 'HELLO A,1,2,3,4,HELLO B,a,b,c,d,HELLO C,x,y,z,w';
let fragments = txt.split(',');

const myMagic = (one, two, three, four) => `this is ${one} and ${two} and ${three} and ${four} as usual`

//

const convert = R.pipe(
    R.splitEvery(5),
    R.chain(
        R.juxt(R.pair(
            R.head,
            R.pipe(R.tail, R.apply(myMagic))
        ))
    )
)

//


console.log(convert(fragments))
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"&gt;&lt;/script&gt;

【讨论】:

  • 谢谢!这里不是 ramda 的用户,但老实说,这段代码对我来说看起来很常见(我知道如果我习惯了它会改变,但仍然......)
  • 我也不是 ramda 大师,所以我相信这可以简化...让我们 ping @ScottSauyet ;)
【解决方案4】:

如果你喜欢ramda,这样的事情可能会有所帮助

const data = 'HELLO&ho&hy&hu&hq&HELLO&ho&hy&hu&hq&HELLO&ho&hy&hu&hq&HELLO&ho&hy&hu&hq&hx'

const toString = ([head, a, b, c, d] = []) => [
  head,
  `this is ${a} and ${b} and ${c} and ${d} as usual`,
]

const magic = R.pipe(
  R.split('&'),
  R.splitEvery(5),
  R.map(toString),
  R.unnest,
  R.init, // to remove last malformed item
);

console.log(
  'result : ',
  magic(data),
);
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"&gt;&lt;/script&gt;

【讨论】:

    猜你喜欢
    • 2012-08-29
    • 1970-01-01
    • 2017-06-27
    • 2015-10-24
    • 2012-11-18
    • 1970-01-01
    • 2017-06-25
    • 1970-01-01
    • 2021-12-22
    相关资源
    最近更新 更多