Схема: Реализация n-аргумента составляет сгиб использования

Не это, что [SelectMany][1] для?

enum1.SelectMany(
    a => a.SelectMany(
        b => b.SelectMany(
            c => c.Select(
                d => d.Name
            )
        )
    )
);
9
задан Paul Hollingsworth 7 November 2009 в 04:21
поделиться

4 ответа

Вы можете попробовать эту версию (используется reduce из SRFI 1 ):

(define (compose . fns)
  (define (make-chain fn chain)
    (lambda args
      (call-with-values (lambda () (apply fn args)) chain)))
  (reduce make-chain values fns))

Это не ракетостроение : когда я разместил это на IRC-канале #scheme, Эли заметил, что это стандартная реализация compose . :-) (В качестве бонуса он также хорошо работал с вашими примерами.)

4
ответ дан 3 November 2019 в 01:01
поделиться

Хотя было бы неплохо, если бы «пустой» список был передан функции идентификации, передача этого, похоже, приведет к следующему, что не так уж плохо:

(define compose-n
  (lambda (first . rest)
    (foldl compose first rest)))

((compose-n cdr cdr car) '(1 2 3))

((compose-n list identity identity) 1 2 3)
1
ответ дан 3 November 2019 в 01:01
поделиться

Проблема в том, что вы пытаетесь смешивать процедуры разной степени сложности. Вы, вероятно, захотите составить список, а затем сделать следующее:

(((compose-n (curry list) identity) 1) 2 3)

Но это не совсем удовлетворительно.

Вы можете рассмотреть n-арную функцию идентичности:

(define id-n
  (lambda xs xs))

Затем вы можете создать процедуру compose специально для составления n-арной functions:

(define compose-nary
  (lambda (f g)
    (lambda x
      (flatten (f (g x))))))

Составление произвольного числа n-мерных функций с помощью:

(define compose-n-nary
  (lambda args
    (foldr compose-nary id-n args)))

Что работает:

> ((compose-n-nary id-n list) 1 2 3)
(1 2 3)

EDIT: Это помогает мыслить в терминах типов. Давайте придумаем обозначение типов для наших целей. Мы обозначим тип пар как (A. B) , а тип списков как [*] , с условием, что [*] эквивалентно (A. [*]) , где A - это тип автомобиля списка (т. е. список - это пара атома и списка). Далее обозначим функции как (A => B) , что означает «принимает A и возвращает B». => и . оба связаны справа, поэтому (A. B. C) равно (A. (B. C)) .

Итак ... с учетом этого, вот тип списка (читается :: как «имеет тип»):

list :: (A . B) => (A . B)

И вот идентификатор:

identity :: A => A

Есть различие по типу. Тип list состоит из двух элементов (т.е. тип списка имеет вид * => * => * ), а тип identity состоит из одного type (тип удостоверения имеет вид * => * ).

Состав имеет этот тип:

compose :: ((A => B).(C => A)) => C => B

Посмотрите, что произойдет, когда вы примените составить к списку и идентификатору . объединяется с доменом функции list , поэтому это должна быть пара (или пустой список, но мы это не будем рассматривать). C объединяется с доменом функции identity , поэтому он должен быть атомом. Таким образом, композиция этих двух элементов должна быть функцией, которая принимает атом C и дает список B . Это не проблема, если мы дадим этой функции только атомы, но если мы дадим ей списки, она задохнется, потому что ожидает только один аргумент.

Вот как помогает карри:

curry :: ((A . B) => C) => A => B => C

Применить карри в список , и вы увидите, что произойдет. Вход в список объединяется с (A. Б) . Результирующая функция принимает атом (автомобиль) и возвращает функцию. Эта функция, в свою очередь, принимает оставшуюся часть списка (cdr типа B ) и, наконец, возвращает список.

Важно отметить, что функция curried list имеет тот же вид как идентификатор , поэтому они могут быть составлены без проблем. Это работает и в обратном направлении. Если вы создаете функцию идентификации, которая принимает пары, ее можно составить с помощью обычной функции list .

так что их можно составить без проблем. Это работает и в обратном направлении. Если вы создаете функцию идентификации, которая принимает пары, ее можно составить с помощью обычной функции list .

так что их можно составить без проблем. Это работает и в обратном направлении. Если вы создаете функцию идентификации, которая принимает пары, ее можно составить с помощью обычной функции list .

2
ответ дан 3 November 2019 в 01:01
поделиться

The OP mentioned (in a comment to my answer) that his implementation of Scheme does not have call-with-values. Here's a way to fake it (if you can ensure that the symbol is never otherwise used in your program: you can replace it with (void), (if #f #f), or whatever you like that's not used, and that's supported by your implementation):

(define (values . items)
  (cons '<values> items))

(define (call-with-values source sink)
  (let ((val (source)))
    (if (and (pair? val) (eq? (car val) '<values>))
        (apply sink (cdr val))
      (sink val))))

What this does is that it fakes a multi-value object with a list that's headed by the symbol. At the call-with-values site, it checks to see if this symbol is there, and if not, it treats it as a single value.

If the leftmost function in your chain can possibly return a multi-value, your calling code has to be prepared to unpack the -headed list. (Of course, if your implementation doesn't have multiple values, this probably won't be of much concern to you.)

4
ответ дан 3 November 2019 в 01:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: