Понимание функций свертывания и уменьшения в схеме

#javascript #scheme #lisp #reduce #fold

#javascript #схема #lisp #уменьшить #сворачивание

Вопрос:

У меня общий вопрос о scheme и lisp. Как fold и reduce функции должны работать?

При использовании схемы guile (use-modules (srfi srfi-1)) вы можете использовать это:

 guile> (fold cons '() '(1 2 3 4))
> (4 3 2 1)
guile> (fold cons '(1 2 3 4) '())
> (1 2 3 4)
  

Я работаю над функцией fold в моем lisp на JavaScript, я хочу создать единую функцию как для reduce, так и для fold (функция вернет одну из этих функций).

Но как они должны работать? В моем коде я проверяю, не равен ли первый список нулю или он не закончился, но здесь вы передаете пустой список (первый код). Работает ли fold вместо этого во втором списке и проверяет, не закончилось ли это, или на обоих, поскольку reverse также работает? Сколько раз fold выполнялся при вызове с ‘() в качестве значения инициализации, единицы или он обрабатывает весь первый аргумент?

Вот моя функция уменьшения:

 function reduce(fn, list, init = nil) {
    if (isEmptyList(list) || isNull(list)) {
        return list;
    }
    let result = init;
    let node = list;
    if (init === null) {
        result = list.car;
        node = list.cdr;
    }
    return (function loop() {
        function next(value) {
            result = value;
            node = node.cdr;
            return loop();
        }
        if (node === nil || !(node instanceof Pair)) {
            if (typeof result === 'number') {
                return LNumber(result);
            }
            return resu<
        }
        const item = node.car;
        const value = fn(result, item);
        if (isPromise(value)) {
            return value.then(next);
        } else {
            return next(value);
        }
    })();
}
  

верны ли приведенные ниже результаты для reduce ?

 lips> (reduce cons '(1 2 3 4) nil)
((((nil . 1) . 2) . 3) . 4)
lips> (reduce list '(1 2 3 4) nil)
((((nil 1) 2) 3) 4)
lips>
  

Как функция fold должна работать и выглядеть в JavaScript? Какова точная логика для fold и reduce в схеме?

Вот еще один пример для хитрости:

 guile> (fold-right append '(1 2 3 4) '())
(1 2 3 4)
lips> (reduce append '(1 2 3 4) '())
(1 2 3 4)
  

это работает так же в моем lisp, означает ли это, что мое сокращение правильное? Как проверить, правильно ли работает моя функция?

У меня есть одна проблема, связанная с лукавством:

 guile> (fold-right list '(1 2 3 4) '())
> (1 2 3 4)
guile> (fold list '(1 2 3 4) '())
> (1 2 3 4)
  

но в моем lisp:

 lips> (reduce list '(1 2 3 4) '())
((((() 1) 2) 3) 4)
  

является ли fold-right на самом деле reduce? Потому что этот код в guile дает тот же результат, что и мой reduce:

 guile> (list (list (list (list '() 1) 2) 3) 4)
> ((((() 1) 2) 3) 4)
  

Ответ №1:

https://www.gnu.org/software/guile/manual/html_node/SRFI_002d1-Fold-and-Map.html

процедура свертки процесса инициализации lst1 lst2 Scheme:
процедура схемы lst1 lst2 с откидной правой обработкой:

Примените proc к элементам lst1 lst2, чтобы создать результат, и верните этот результат.

Каждый вызов proc является (proc elem1 elem2предыдущий), где elem1 из lst1, elem2 из lst2 и так далее. предыдущий — это возврат из предыдущего вызова proc или заданный init для первого вызова. Если какой-либо список пуст, возвращается только инициализация.

fold работает через элементы списка от первого до последнего. Ниже показано обращение к списку и выполняемые им вызовы,

 (fold cons '() '(1 2 3))

(cons 1 '())
(cons 2 '(1))
(cons 3 '(2 1)
⇒ (3 2 1)
  

fold-right работает через элементы списка от последнего к первому, т. е. справа. Так, например, следующая строка находит самую длинную строку и последнюю среди равных по длине,

 (fold-right (lambda (str prev)
              (if (> (string-length str) (string-length prev))
                  str
                  prev))
            ""
            '("x" "abc" "xyz" "jk"))
⇒ "xyz"
  

Свертки scheme поддерживают несколько списков, но я покажу вам, как настроить вашу реализацию JavaScript, чтобы она работала с одним списком —

 function reduce (fn, init, list) {
  if (isNull(list))
    return init
  else
    return reduce(fn, fn(list.car, init), list.cdr)
}

function reduceRight (fn, init, list) {
  if (isNull(list))
    return init
  else
    return fn(list.car, reduceRight(fn, init, list.cdr))
}
  

Поддержка нескольких списков достаточно проста благодаря поддержке JavaScript параметров rest и аргументов распространения

 function some (fn, list) {
  if (isNull(list))
    return false
  else
    return fn(list.car) || some(fn, list.cdr)
}

function reduce (fn, init, ...lists) {
  if (some(isEmpty, lists))
    return init
  else
    return reduce
             ( fn
             , fn (...lists.map(l => l.car), init)
             , lists.map(l => l.cdr)
             )
}

function reduceRight (fn, init, ...lists) {
  if (some(isEmpty, lists))
    return init
  else
    // exercise left for reader
    // ...
}