Как перевести код из Lisp (MIT Schema) в JavaScript с помощью Ramda? - PullRequest
0 голосов
/ 10 января 2019

Я сейчас учу себя функциональному программированию.

Я пытаюсь перевести следующее:

(define a 3)
(define b (+ a 1))

(* (cond ((> a b) a)
         ((< a b) b)
         (else -1))
   (+ a 1))

в JavaScript (с использованием Ramda).

Можно использовать вложенные троицы, но мне нравится использовать функцию cond от Ramda. Вот что я сделал:

const a = 3;
const b = 3 + 1;

cond([[() => a > b, () => a], [() => a < b, () => b], [T, () => -1]])() * (a + 1)

Проблема, с которой я столкнулся, заключается в том, что мне пришлось использовать эти функции (например, () => 3), а не только их значение (например, a).

Есть ли способ избежать этих функций? Или есть другой лучший способ сделать это в JavaScript (может быть, даже без Rambda)?

Я бы хотел избежать таких утверждений, как if, for и switch.

Альтернативным способом решения этой проблемы будет использование:

import gt from "ramda/src/gt";
import lt from "ramda/src/lt";

const a = () => 3;
const b = () => a() + 1;


cond([[gt(a, b), a], [lt(a, b), b], [T, () => -1]])() * (a() + 1);

Что усложняет a и b, поскольку они всегда должны вызываться (см. a() + 1).

EDIT:

По какой-то причине последний код, в котором я определяю a и b как функции, не работает ?

Ответы [ 3 ]

0 голосов
/ 10 января 2019

Причина, по которой вам не нужно оборачивать все в thunks (то есть функции, которые не принимают аргументов) в Scheme, заключается в том, что cond - это макрос, который в вашем случае расширяется до вложенных if s:

(if (> a b) a
  (if (< a b) b
    -1))

Так что нет, если вы хотите избежать троичных операторов и оборачивать все в гущу событий, у вас нет особого выбора с vanilla JS.

Если вы не возражаете против использования нестандартного JS, вы можете реализовать cond с макросом с Sweet.js ...

0 голосов
/ 10 января 2019

Я думаю, что вы работаете с небольшим заблуждением. cond разработан, чтобы дать вам функцию. Он действительно не предназначен для создания скалярного значения для выражения. Конечно, это можно сделать, просто вызвав вызываемую функцию. Но это не главное. Более того, он действительно не предназначен для вызова с нулевыми функциями (за исключением случая передачи T для условия по умолчанию). Опять же, вы можете сделать это, но это работает против структуры инструмента.

Хотя Рамда черпает вдохновение из функциональных языков как в стиле LISP, так и в стиле ML, и хотя я лично больше поклонник первого, Рамда ближе к миру ML, особенно к Haskell. Таким образом, основное действие, которое он должен поддерживать, - это создание функций путем составления других функций.

Если бы я решил эту проблему, я бы не стал использовать Рамду вообще, выбрав что-то вроде этого:

const larger = (a, b) => (a > b) ? a : (b > a) ? b : -1
const foo = (a, b) => (a + 1) * larger(a, b)

foo(3, 4) //=> 16
foo(6, 3) //=> 42
foo(3, 3) //=> -4

Или, если мне не нужно повторно использовать larger, я мог бы вставить это так:

const foo = (a, b) => (a + 1) * ((a > b) ? a : (b > a) ? b : -1)

Конечно, я могу написать это в «Рамде» и бессмысленно:

const larger = cond([[gt, unapply(head)], [lt, unapply(last)], [T, always(-1)]])
const foo = converge(multiply, [inc, larger])

Или снова я мог бы вставить larger или заменить unapply(head) на nthArg(0) и unapply(last) на nthArg(1).

Но ни одна из этих опций не так удобна для чтения, как оригинал. Рамда ничего не добавляет здесь, что я вижу. Обратите внимание, что я большой поклонник Рамды; Я основал библиотеку и являюсь одним из ее основных авторов. Но я не думаю, что это следует использовать для каждой проблемы.

0 голосов
/ 10 января 2019

Рамда автоматически каррируется, так что вы можете вызвать функцию с некоторыми параметрами и получить новую функцию обратно. Например:

const { pipe, cond, gt, lt, T, always, identity, multiply } = R

const a = 3
const b = 3 + 1

const fn = (a) => pipe(
  cond([
    [gt(a), always(a)],
    [lt(a), identity],
    [T, always(-1)]
  ]),
  multiply(a + 1)
)

const result = fn(a)(b)

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
...