javascript функциональная/декларативная версия цикла for (с этим примером)?

Имея этот код

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

Итак, есть ли способ написать это более декларативно, не изменяя логику?


person GWorking    schedule 22.09.2019    source источник
comment
Определите более декларативный...? Вы должны зацикливаться группами по пять человек. Ни одна из встроенных функций массива этого не делает. Вы могли бы втиснуть это в один из них (возможно), но это просто стоило бы вам ясности ИМХО.   -  person T.J. Crowder    schedule 22.09.2019
comment
@ T.J.Crowder К счастью, мы можем написать наши собственные комбинаторы на Javascript и перенять общие комбинаторы из FP, а затем объединить / составить их, чтобы использовать декларативный алгоритм также для данной задачи.   -  person Iven Marquardt    schedule 22.09.2019
comment
@bob - Да, действительно, FP или что-то еще...   -  person T.J. Crowder    schedule 22.09.2019


Ответы (4)


Вы всегда можете использовать рекурсивную функцию для обхода списков:

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 над ее результатом.

person Bergi    schedule 22.09.2019
comment
Зачем мне flat(), если вы уже возвращаете ...pieces(more)? - person GWorking; 23.09.2019
comment
@GWorking Что flat() ты имеешь в виду? Если вы имеете в виду мой последний абзац, см. ответ пользователя 3297291 о том, что я имел в виду с flatMap. - person Bergi; 23.09.2019
comment
Да, я понимаю, что вы имеете в виду, хотя я вижу ваш вариант в ответе как самый простой, более лаконичный и наглядный, который в конце концов есть над чем подумать. Я использую ваше решение, так что тысяча благодарностей! - person GWorking; 27.09.2019

Для меня самое приятное заключается в использовании некоторых утилит, которые вы можете получить из lodash, ramda или любой другой слегка «функциональной» библиотеки, но с сохранением логики [ a, f(b, c, d, e) ] в обычной функции стрелки. (может быть личным предпочтением)

Шаги, которые необходимо предпринять:

  • Разделите строку на один массив строк (я использую split("&"))
  • Разделить массив строк на массив массивов из 5 строк (chunk(5))
  • Вызов flatMap во внешнем массиве
  • Сопоставьте внутренние массивы, используя ([ head, ...tail]) => [ 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));

person user3297291    schedule 23.09.2019
comment
хорошо, но я бы предпочел назвать его chunk, потому что aperture ramda — это другое. - person georg; 23.09.2019
comment
А, теперь я вижу. Апертура Ramda создает перекрывающиеся группы размером n, а не разделение (что имеет смысл...). Я только что заметил, что Берги тоже упоминает имя chunk :D - person user3297291; 23.09.2019
comment
Мне нравится, когда люди используют монады в JS! - person Iven Marquardt; 23.09.2019
comment
Большое спасибо за подробное объяснение, лично я все еще стараюсь избегать использования внешних библиотек (что я хочу исправить в ближайшее время), но я вижу, что вы здесь делаете, так что спасибо за ваше время :) Тем не менее, сравнивая код с кодом Берги , твой почти в два раза длиннее, вот это меня окончательно смущает - person GWorking; 27.09.2019
comment
Пожалуйста! Для меня функции в Utils — это функции, которые уже были бы доступны в моем проекте, из библиотеки, такой как Ramda, или из собственной библиотеки, как здесь. Поэтому я не вижу, чтобы они удваивали длину;) - person user3297291; 27.09.2019
comment
Да, я полностью согласен, без этих функций код выглядит действительно более лаконичным... Мне определенно нужно прыгнуть в эти библиотеки, еще раз спасибо! - person GWorking; 28.09.2019

Если вам нравится декларативный/функциональный стиль, почему бы не попробовать 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))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

person georg    schedule 22.09.2019
comment
Спасибо! здесь не пользователь ramda, но, честно говоря, этот код кажется мне урезанным (я знаю, что это изменится, если я к нему привыкну, но все же ...) - person GWorking; 23.09.2019
comment
Я тоже не гуру ramda, поэтому я уверен, что это можно упростить... Давайте пропингуем @ScottSauyet ;) - person georg; 23.09.2019

Что-то вроде этого может помочь, если вам нравится 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),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"></script>

person Hitmands    schedule 23.09.2019