Функции Clojure, похоже, не работают должным образом при отправке через список - PullRequest
4 голосов
/ 19 декабря 2011

В Clojure REPL это выражение

( #(for [x %] (+ 100 (second x)))  ['(+ 38) '(+ 48)] )

производит (138 148), как и ожидалось

но это

( #(for [x %] ((first x) 100 (second x))) ['(+ 38) '(+ 48)] )

производит (38 48), что кажется действительно странным.

Оба выражения действительно должны давать одинаковый результат! Что мне не хватает? Буду признателен за любые идеи, чтобы разгадать эту тайну.

Кстати, я пытался использовать 'apply (first x)' и упаковать остальные аргументы в список, но это не имеет значения. Тот же неожиданный результат возвращается.

Кроме того, чтобы убедиться, что + действительно разрешается на входе, я передал в REPL

следующее
( #(for [x %] (resolve (first x) )) '((+ 38) (+ 48)) )

который произвел

 (#'clojure.core/+ #'clojure.core/+)   as expected.

Ответы [ 2 ]

10 голосов
/ 19 декабря 2011
( #(for [x %] ((first x) 100 (second x))) ['(+ 38) '(+ 48)] )

В этом + является символом, а не функцией, потому что он был указан в списке.Однако символы определяются как поиск по карте при вызове в виде функции (так же, как ключевые слова).Так что ('+ 100 38) - это то же самое, что и (get 100 '+ 38).Последний аргумент: «Если вы не можете найти то, что я хочу на карте, верните это».Поскольку 100 не является картой, + использует этот аргумент в качестве возвращаемого значения.

Чтобы заставить его делать то, что вам нужно, у вас есть две опции:

  1. Использование векторов вместо списков в кавычках обеспечивает правильное разрешение +.

    ( #(for [x %] ((first x) 100 (second x))) [[+ 38] [+ 48]] )
    
  2. Разрешите сами, чтобы убедиться, что вы используете функцию + вместо символа +.

    ( #(for [x %] ((resolve (first x)) 100 (second x))) ['(+ 38) '(+ 48)] )
    
3 голосов
/ 19 декабря 2011

Когда вы цитируете список, например '(+ 38), ни один из элементов в списке не оценивается. Таким образом, + является просто символом, а не ссылкой на функцию сложения из clojure.core.

Результат вызова этого символа как функции немного сбивает с толку, особенно если учесть, что вы вызываете его точно с двумя аргументами. Причина была уже объяснена @mange: вызов символа в качестве функции пытается найти символ в первом аргументе, возвращая (необязательный) второй аргумент в качестве значения по умолчанию при сбое поиска:

('x)       ; throws ArityException
('x 1)     ;=> nil
('x 1 2)   ;=> 2
('x 1 2 3) ; throws ArityException

У вас есть несколько вариантов:

  1. Использовать векторы вместо цитируемых списков: [+ 38]. Все элементы вектора оцениваются (как в списке без кавычек), но вектор - это просто структура данных, а не синтаксис для вызова функции, как в списке.
  2. Используйте функцию resolve, чтобы найти функцию, на которую ссылается символ. Обратите внимание, что resolve ищет символ в текущем пространстве имен. Поэтому, если создание списка в кавычках и вызов функции происходят в разных пространствах имен, это может привести к неожиданным результатам (если у вас два разных определения одного и того же символа в разных пространствах имен).
  3. Использовать синтаксическое цитирование вместо простого цитирования в списке и расстановить кавычки в символе (вычисляя его): `(~+ 38)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...