web-dev-qa-db-ja.com

Clojureの高階関数

Clojureは素晴らしいです、私たちは皆これを知っています、しかしそれはポイントではありません。 Haskellのような方法で高階関数を作成および管理する慣用的な方法は何であるか疑問に思います。 Clojureでは、次のことができます。

(defn sum [a b] (+ a b))

だが (sum 1)は関数を返しません:エラーが発生します。もちろん、次のようなことができます。

(defn sum
  ([a] (partial + a)) 
  ([a b] (+ a b)))

この場合:

user=> (sum 1)
#<core$partial$fn__3678 clojure.core$partial$fn__3678@1acaf0ed>
user=> ((sum 1) 2)
3

しかし、それは先に進む正しい方法のようには思えません。何か案は?
私はsum関数の実装について話しているのではなく、より高いレベルの抽象化について話しているのです。従うべき慣用的なパターンはありますか?いくつかのマクロ?マクロを定義する最良の方法ですか、それとも代替ソリューションがありますか?

36

誰かがClojureグループに すでにこれを実装しています を持っています。関数が持つ引数の数を指定でき、その数になるまで関数は自動的にカレーします。

Clojureでこれがデフォルトで発生しない理由は、自動カレー関数よりも可変個引数関数を好むためだと思います。

32
amalloy

Amalloyが提案する機能を少し試してみました。カレーする引数の数を明示的に指定するのは好きではありません。そこで、カスタムマクロを作成しました。これは、高階関数を特定するための古い方法です。

(defn-decorated old-sum
  [(curry* 3)]
  [a b c]
  (+ a b c))

これは私の新しいマクロです:

(defmacro defn-ho
  [fn-name & defn-stuff]
  (let [number-of-args (count (first defn-stuff))]
    `(defn-decorated ~fn-name [(curry* ~number-of-args)] ~@defn-stuff)))

そして、これは新しい暗黙の方法です。

(defn-ho new-sum [a b c] (+ a b c))

(カレー)などの痕跡がないことがわかるように、前と同じようにcurrified関数を定義するだけです。

みんな、どう思いますか?アイデア?提案?さようなら!

アルフェド

編集:docstringに関するアマロイの問題に従ってマクロを変更しました。これは更新されたバージョンです:

(defmacro defhigh
  "Like the original defn-decorated, but the number of argument to curry on
  is implicit."
  [fn-name & defn-stuff]
  (let [[fst snd] (take 2 defn-stuff)
         num-of-args (if (string? fst) (count snd) (count fst))]
    `(defn-decorated ~fn-name [(curry* ~num-of-args)] ~@defn-stuff)))

2番目のバインディング内のifステートメントは好きではありません。それをより簡潔にすることについてのアイデアはありますか?

8

これにより、必要なことを実行できます。

(defn curry
  ([f len] (curry f len []))
  ([f len applied]
    (fn [& more]
      (let [args (concat applied (if (= 0 (count more)) [nil] more))]
        (if (< (count args) len)
          (curry f len args)
          (apply f args))))))

使用方法は次のとおりです。

(def add (curry + 2)) ; read: curry plus to 2 positions
((add 10) 1) ; => 11

[nil]の条件は、すべてのアプリケーションがカレー状態への前進を確実にすることを目的としています。その背後には長い説明がありますが、私はそれが役に立つと思いました。このビットが気に入らない場合は、argsを次のように設定できます。

[args (concat applied more)]

JavaScriptとは異なり、渡された関数のアリティを知る方法がないため、必要な長さを指定する必要があります。これは、関数が複数のアリティを持つ可能性があるClojure [Script]では非常に理にかなっています。

0
Mario