2016-09-04 12 views
2

Принимая три функции ниже, реализованные в Haskell и Clojure соответственно:В чем разница между этими функциями, реализованными с карри и преобразователями?

f :: [Int] -> Int 
f = foldl1 (+) . map (*7) . filter even 

(defn f [coll] 
    ((comp 
    (partial reduce +) 
    (partial map #(* 7 %) 
    (partial filter even?)) coll)) 

(defn f [coll] 
    (transduce 
    (comp 
     (filter even?) 
     (map #(* 7 %))) 
    + coll)) 

, когда они применяются к списку как [1, 2, 3, 4, 5] все они возвращаются 42. Я знаю, что оборудование, стоящее за первым 2, похоже на то, что map ленив в Clojure, но третий использует преобразователи. Может ли кто-нибудь показать промежуточные шаги для выполнения этих функций?

+1

Я не думаю, что 'сокращение' является стандартной функцией Haskell. Хотя я предполагаю, что вы, вероятно, имеете в виду один из ['foldr1'] (https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Foldable.html#v:foldr1) или [' foldl1'] (https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Foldable.html#v:foldl1). – Alec

+0

Вы правы, я их перепутал. –

+1

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

ответ

3

Промежуточные этапы между вторым и третьим примерами являются такими же для данного конкретного примера. Это связано с тем, что map и фильтр реализованы как ленивые преобразования последовательности в последовательность, о чем вы, несомненно, уже знаете.

Варианта измерительного преобразователя карты и фильтра определяется с использованием того же существенных функциональными возможностями как версии не-преобразователей, за исключением того, что способ, которым они «Conj» (или нет, в случае фильтра) на поток результата является определенном в другом месте. Действительно, если и посмотреть на источник для карты, то используются explicit data-structure construction functions, тогда как transducer variant не использует такие функции - они передаются через rf. Явно используя cons в версиях без преобразователя означает, что они всегда будет иметь дело с последовательностями

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

(def process (comp (filter even) 
        (map #(* 7 %)))) 

(defn f [coll] (transduce process + collection)) 

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


Это может произойти с вами, что вы можете просто переписать

(defn process [coll] 
    ((comp 
    (partial map #(* 7 %) 
    (partial filter even?)) coll)) 

(reduce + (process coll)) 

И получить тот же эффект; это правда. Когда ваш вход всегда представляет собой последовательность (или всегда тот же тип потока/вы знаете, какой поток он будет), возможно, нет веской причины для создания преобразователя. Но сила повторного использования может быть продемонстрирована здесь (предположит, что процесс представляет собой преобразователь)

(chan 1 process) ;; an async channel which runs process on all inputs 

(into [] process coll) ;; writing to a vector 

(transduce + process coll) ;; your goal 

Мотивация преобразователей, по сути, чтобы остановить, чтобы писать новые функции сбора для различных типов коллекций.Рич Хикки упоминает свое разочарование писать функции как карта < карта> mapcat < mapcat>, и так далее в библиотеке ядра асинхронного - , что карта и mapcat являются, уже определены, а потому, что они предположить, что они работают на последовательностях (которые явным образом связаны с cons), они не могут применяться к асинхронным каналам. Но каналы могут поставлять свои собственные rf в версии преобразователя, чтобы они могли повторно использовать эти функции.

 Смежные вопросы

  • Нет связанных вопросов^_^