Каким образом функционируют карты Clojure? - PullRequest
4 голосов
/ 22 марта 2019

В своем выступлении «Возможно, нет» Рич Хики заявляет:

карты являются (математическими) функциями!

в Clojure, мы можем напрямую написать,и вызвать

({: a 1: b 2}: b) => 2

Однако у меня есть ощущение, что они на самом деле не являются функциями Clojure первого класса илиони?

Я могу вызвать карту с помощью ключевого слова, или наоборот:

user=> (:b {:a 1 :b 2 :c 3})
2
user=> ({:a 1 :b 2 :c 3} :b)
2

Но я не могу использовать применить так, как кажется:

user=> (apply #(:b %) {:a 1 :b 2 :c 3})
ArityException Wrong number of args (3) passed to: user/eval1762/fn--1763  clojure.lang.AFn.throwArity (AFn.java:429)

user=> (apply #({:a 1 :b 2 :c 3} %) :b)
IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Keyword  clojure.lang.RT.seqFrom (RT.java:542)

И я также не могу применить ключевое слово непосредственно к карте:

user=> (apply {:a 1 :b 2 :c 3} :b)
IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Keyword  clojure.lang.RT.seqFrom (RT.java:542)

Так что они функционируют только в математическом смысле, или их больше в смысле применения ключевого слова, аналогичного«нормальная» функция clojure?

Ответы [ 3 ]

9 голосов
/ 22 марта 2019

Карты являются функциями клавиш, как в первых двух примерах.Карты реализуют интерфейс IFn, как «обычные» функции Clojure.

Причина, по которой apply не работает в ваших примерах, связана с передачей аргументов «sequence» ввторая позиция.

(apply #(:b %) {:a 1 :b 2 :c 3})

В этом примере аргумент карты превращается в последовательность векторов / кортежей ключ / значение, поэтому их можно применять к #(:b %) (чтовсе равно не будет работать, потому что эта анонимная функция принимает только один аргумент).Вот как будет выглядеть карта, если ее превратить в последовательность и применить в качестве аргументов к функции:

user=> (seq {:a 1 :b 2 :c 3})
([:a 1] [:b 2] [:c 3])

Этот второй пример не работает, поскольку :b не является последовательностью - это одиночныйключевое слово.Это работает, хотя:

user=> (apply {:a 1 :b 2 :c 3} [:b])
2

Обратите внимание, что вызов map-as-function с apply и последовательностью ключевых слов на самом деле не имеет практического смысла.

8 голосов
/ 22 марта 2019

A математическая функция - это набор упорядоченных пар, в которых нет отдельных пар с одним и тем же первым элементом. Определенная таким образом математическая функция принимает только один аргумент.

Карты Clojure являются самоочевидными математическими функциями, поскольку не существует отдельных пар ключ-значение с одним и тем же ключом. Кроме того, карты Clojure имеют конечный домен (набор keys) и диапазон / совместный домен (набор values).

Как заявил @ TaylorWood , карты Clojure являются Clojure функциями, поскольку они реализуют clojure.lang.IFn . Они делают это как операторы, дающие значение для данного ключа, что соответствует их интерпретации как математических функций.

Ваш синтаксис apply неверен. Вы можете написать

user=> (apply {:a 1, :b 2} [:a])
1

или

user=> (apply {:a 1, :b 2} :a []) 
1

Вы можете указать некоторые аргументы для функции (первый аргумент apply), встроенные. Остальные содержат последовательность, которая является последним аргументом apply. Поскольку здесь есть только один аргумент, мы можем поместить его как встроенный или один в конечную последовательность.

Аналогичные соображения применимы к использованию ключевых слов в качестве аргументов.


Ответ на комментарии @ AlanThompson

  1. Я слушал и читал Рич Рич , к которому относится этот вопрос. Мое определение функции согласуется с его.

  2. Мое утверждение, что "Математическая функция , определенная таким образом , принимает только один аргумент" само по себе очевидно.

  3. Вы могли бы утверждать, что мое определение не является общепринятым. Но даже это неверно. Если вы прочитаете Multivariate_function часть статьи Википедии, на которую мы оба ссылаемся, вы найдете

Более формально, функция от n переменных - это функция, область которой набор n-кортежей

...

При использовании функции обозначения обычно опускаются скобки окружающие кортежи, пишущие f(x1 , x2) вместо f((x1 , x 2)).

Другими словами, идея о том, что (математическая) функция принимает несколько аргументов, является безвредной неформальностью, которая допускает более простые обозначения.

Где это оставляет языки программирования? Большинство из них имеют функции любой арности: в случае Clojure - любой конечный набор арностей, включая бесконечный.

Самым простым способом рассмотрения функций Clojure является то, что они принимают встроенную последовательность аргументов, возможно, пустую. Можно было бы сформулировать математические функции следующим образом: сопоставления последовательностей вещей с вещами. Но это просто не сделано .

4 голосов
/ 22 марта 2019

Я не уверен, какова практическая цель этого вопроса, но это интересно с академической точки зрения.

Ключевое слово "function" :b в следующем является функцией 1аргумент:

(:b {:a 1 :b 2})  => 2

Это похоже на эту функцию:

(defn get-b [m] (get m :b)) ; takes a single map arg

(get-b {:a 3 :b 33})  ; only 1 arg, so no problem
   => 33

Специальная форма apply переписывает вызов функции от наличия одного аргумента коллекции до нескольких отдельных аргументов.Сравнивая до и после, мы получаем:

(apply some-fn [1 2 3] )  ; using  apply
      (some-fn  1 2 3  )  ; without apply (note missing square brackets!)

Итак, apply позволяет вам «распространять» аргументы из коллекции , как если бы вы ввели их какотдельные, отдельные аргументы в вызове функции.Если ваша коллекция содержит только 1 элемент, использование apply имеет небольшой эффект:

(apply :b [{:a 3 :b 33}] ) ; a collection of 1 element
      (:b  {:a 3 :b 33}  ) ; only 1 arg, so no problem

Поскольку функция: b занимает всего 1 аргумент, следующее не будет работать:

(apply #(:b %) [{:a 1 :b 11} {:a 2 :b 22} {:a 3 :b 33}])  ; takes only 1 arg, not 3 args

ОднакоВы можете вызывать функцию 1-arg :b несколько раз, используя map или mapv:

(mapv :b [{:a 1 :b 11} {:a 2 :b 22} {:a 3 :b 33}])  ; takes only 1 arg, not 3 args

   => [11 22 33]

Те же правила действуют в обратном случае.Карта типа {:a 1 :b 2} является функцией с 1 аргументом, поэтому ее нельзя вызывать с 3 аргументами:

(apply {:a 1, :b 2, :c 3} [:c :b :a])  ; 3 args to single-arg fn, so fails

, но это прекрасно работает:

(mapv {:a 1, :b 2, :c 3} [:c :b :a]) => [3 2 1]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...