英和訓練

著作権は原文に属します

Learn Clojure - Functions 関数

Creating Functions 関数を作る

Clojure is a functional language. Clojure関数型言語の一つだ。Functions are first-class and can be passed-to or returned-from other functions. 関数は第一級であり、他の関数に渡されたり、関数から返されたりする。Most Clojure code consists primarily of pure functions (no side effects), so invoking with the same inputs yields the same output. ほとんどのClojureコードは主に(副作用のない)純粋な関数らによって構成されているから、同じ入力で呼び出せば同じ出力を生じる。

defn defines a named function: defn は名前付き関数を定義する。

;;    name   params         body 名前、パラメータ、本体
;;    -----  ------  -------------------
(defn greet  [name]  (str "Hello, " name) )

This function has a single parameter name, however you may include any number of arguments in the params vector. この関数は一つのパラメータnameを持っているが、パラメータのベクタには任意個の引数を含むことができる。

Invoke a function with the same name of the function in "function position" (the first element of a list): 関数を呼び出すには、その関数の名前を「関数の位置」(つまりリストの第1要素)に置けばいい。

user=> (greet "students")
"Hello, students"

Multi-arity functions 複数のアリティを持つ関数

Functions can be defined to take different numbers of arguments (different "arity"). 関数は異なる個数の引数を取れるように(複数の異なる「アリティ」について)定義できる。 Different arities must all be defined in the same defn - using defn more than once will replace the previous function. 異なるアリティらは同じ一つのdefn内で定義されなければならない。もしdefnを複数回使えば、以前の関数を置き換えることになる。

Each arity is a list ([args*] body*).それぞれのアリティはこの形のリストだ。One arity can invoke another. 一つのアリティから別のアリティを呼び出すことは可能である。

(defn messenger
  ([]     (messenger "Hello world!"))
  ([msg]  (println msg)))

This function declares two arities (0 argument and 1 argument). この関数は、(引数0個と引数1個の)2つのアリティを宣言している。The 0-argument arity calls the 1-argument arity with a default value to print. 引数0個のアリティは、印字するデフォルト値とともに、引数1個のアリティを呼び出している。We invoke these functions by passing the appropriate number of arguments: こうして定義された関数は、対応する個数の引数を与えることで呼び出せる。

user=> (messenger)
Hello world!
nil

user=> (messenger "Hello class!")
Hello class!
nil

Variadic functions 可変長引数関数

Functions may also define a variable number of arguments - this is known as a "variadic" function. 関数は変動する個数の引数について定義することもできる。このような関数は、「可変長引数」関数と呼ばれている。The variable arguments must occur at the end of the argument list. 個数の変動する引数は、引数リストの最後に現れなければならない。They will be collected in a sequence for use by the function. それらの引数は、一つのシーケンスにまとめられて、その関数で使われる。

The beginning of the variable arguments is marked with &. 可変長引数の始まりは&で表される。

(defn hello [greeting & who]
  (println greeting who))

This function takes an argument greeting and a variable number of arguments (0 or more) that will be collected in a list named who. この関数は引数greetingを受け取るほか、(0個以上の)任意個の引数を取ってwhoと名づけられたリストにまとめる。We can see this by invoking it with 3 arguments: 次のように、3個の引数を与えて見てみよう。

user=> (hello "Hello" "world" "class")
Hello (world class)

You can see that when println prints who, it is printed as a list of two elements that were collected. この例で、printlnwhoを印字した有様から、whoが、収集された2つの要素を持つ1つのリストとして印字されていることが確認できる。

Anonymous Functions 無名関数

An anonymous function can be created with fn: fnを使えば無名関数を作ることができる。

;;    params         body パラメータ、本体
;;   ---------  -----------------
(fn  [message]  (println message) )

Because the anonymous function has no name, it cannot be referred to later. 無名関数には名前がないので、後から参照することはできない。Rather, the anonymous function is typically created at the point it is passed to another function. 無名関数の典型的な使い方は、それが他の関数に渡される位置で作成する使い方である。

Or it's possible to immediately invoke it (this is not a common usage): または、(あまり行われないが、)無名関数を次のように直ちに呼び出すこともできる。

;;     operation (function)             argument 関数、引数
;; --------------------------------  --------------
(  (fn [message] (println message))  "Hello world!" )

;; Hello world!

Here we defined the anonymous function in the function position of a larger expression that immediately invokes the expression with the argument. ここでは無名関数が、より大きな式の中の関数の位置で定義され、その式において引数とともに呼び出されている。

Many languages have both statements, which imperatively do something and do not return a value, and expression which do. 多くの言語が、値を返さないものである文と、そうではなく値を返すものである式との両方を持っている。Clojure has only expressions that return a vlaue. Clojureには値を返す式だけがある。We'll see later that this includes even flow control expression like if. 後の説明において、ifのようなフロー制御の式についてもそれが言えることを見る。

defn vs fn defnとfnの違い

It might be useful to think of defn as a contraction of def and fn. defndeffnを縮めたものだと考えるといいかもしれない。The fndefines the function and the def binds it to a name. fnが関数を定義して、defがそれを名前へと束縛する。These are equivalent: つまり以下の2つは等しい。

(defn greet [name] (str "Hello, " name))

(def greet (fn [name] (str "Hello, " name)))

Anonymous function syntax 無名関数の構文

There is a shorter form for the fn anonymous function syntax implemented in the Clojure reader: #(). #()は、fnによる無名関数の構文の短縮形としてClojureのリーダに実装されているものだ。This syntax omits the parameter list and names parameters based on their position. この構文はパラメータのリストを省略し、パラメータらをその位置によって命名する。

  • % is used for a single argument これは単一の引数に対して使われる
  • %1, %2, %3, etc are used for multiple arguments これらは複数の引数について使われる
  • %& is used for any remaining (variadic) arguments これは残りの(可変長な)引数について使われる

Nested anonymous functions would create an ambiguity as the parameters are not named, so nesting is not allowed. パラメータに名前がないので、ネストした無名関数についてはパラメータの指定が曖昧になってしまう。よってネストすることは許されていない。

; Equivalent to 次の式と等しい: (fn [x] (+ 6 x))
#(+ 6 %)

;; Equivalent to 次の式と等しい: (fn [x y] (+ x y))
#(+ %1 %2)

;; Equivalent to 次の式と等しい: (fn [x y & zs] (println x y zs))
#(println %1 %2 %&)

Gotcha 陥りがちな危険

One common need is an anonymous function that takes an element and wraps it in a vector. 要素を取ってベクタで包むために無名関数はしばしば使われる。You might try writing that as: それを次のように書こうとするかもしれない。

;; DO NOT DO THIS こうやってはいけない
#([%])

This anonymous function expands to the equivalent: この無名関数が展開される等価な式は次のものだ。

(fn [x] ([x]))

This form will wrap in a vector and try to invoke the vector with no arguments (the extra pair of parentheses). このフォームはベクタで囲った上でそのベクタを引数なしで呼び出そうとする意味に(余計な括弧のせいで)なってしまう。Instead: 次のようにするとよい。

;; Instead do this: こうする。
#(vector %)

;; or this: こうしてもいい。
(fn [x] [x])

;; or most simply just the vector function itself: または、最も単純に、vector関数そのものでもいい。
vector

Applying Functions 関数適用

apply

The apply function invokes a function with 0 or more fixed arguments, and draws the rest of the needed arguments from a final sequence. apply関数は、0個以上の固定された引数に加え、最後に与えられたシーケンスの内容を残りの引数として関数を呼び出す。The final argument must be a sequence. 最後の引数は必ずシーケンスでなければならない。

(apply f '(1 2 3 4))    ;; same as  (f 1 2 3 4) と同じ
(apply f 1 '(2 3 4))    ;; same as  (f 1 2 3 4) と同じ
(apply f 1 2 '(3 4))    ;; same as  (f 1 2 3 4) と同じ
(apply f 1 2 3 '(4))    ;; same as  (f 1 2 3 4) と同じ

All 4 of these calls are equivalent to (f 1 2 3 4). 上記4つはどれも、(f 1 2 3 4)とするのと等しい。apply is useful when arguments are handed to you as a sequence but you must invoke the function with the values in the sequence. 与えるべき引数がシーケンスとして得られたが、そのシーケンスの中の値によって関数を呼び出したいという時にapplyは便利だ。

For example, you can use apply to avoid writing this: 例えば、applyを使えば次のように書かずにすむ。

(defn plot [shape coords]   ;; coords is [x y] 座標は[x y]の形をしている
  (plotxy shape (first coords) (second coords)))

Instead you can simply write: applyを使えば次のように書ける。

(defn plot [shape coords]
  (apply plotxy shape coords))

Locals and Closures ローカル変数とクロージャ

let

let binds symbols to values in a "lexical scope". letは「レキシカルスコープ」でシンボルを束縛する。A lexical scope creates a new context for names, nested inside the surrounding context. あるレキシカルスコープは、外側で囲うコンテキストの中にネストして、名前の新しいコンテキストを作成する。Names defined in a let take precedence over the names in the outer context. あるletの中で定義された名前らは、より外側のコンテキストにある名前に対して上位の優先度を持つ。

;;      bindings     name is defined here 束縛、名前はここで定義される
;;    ------------  ----------------------
(let  [name value]  (code that uses name))

Each let can define 0 or more bindings and can have 0 or more expressions in the body. 各letは0個以上の束縛を定義し、本体部に0個以上の式を持つことができる。

(let [x 1
      y 2]
  (+ x y))

This let expression creates two local bindings x and y. このlet式は2つのローカル束縛xyを作り出す。The expression (+ x y) is in the lexical scope of the let and resolves x to 1 and y to 2. 式(+ x y)はそのletのレキシカルスコープの内にあり、xは1へ、yは2へと解決される。Outside the let expression, x and y will have no continued meaning, unless they were already bound to a value. let式の外側では、(すでに何らかの値に束縛されているのでなければ、)xとyはもはや意味を保たない。

(defn messenger [msg]
  (let [a 7
        b 5
        c (clojure.string/capitalize msg)]
    (println a b c)
  ) ;; end of let scope letのスコープの終わり
) ;; end of function 関数の終わり

The messenger function takes a msg argument. このmessenger関数は引数msgを取る。Here the defn is also creating lexical scope for msg - it only has meaning within the messenger function. このdefnはまた、msgのためのレキシカルスコープを作ってもいる。msgが意味を持つのはこの、messenger関数の中でだけだ。

Within that function scope, the let creates a new scope to define a, b, and c. 関数のそのスコープの内側において、上記letが、abcを定義する新しいスコープを作っている。If we tried to use a after the let expression, the compiler would report an error. もしもこのlet式が終わったあとでaを使おうとしたならば、コンパイラがエラーを通知することだろう。

Closures クロージャ

The fn special form creates a "closure". 特殊形式fnは「クロージャ」を作る。It "closes over" the surrounding lexical scope (like msg, a, b, or c above) and captures their values beyound the lexical scope. fnは、(上で見た例におけるmsgやaやbやcのように、)囲っているレキシカルスコープを「閉包」し、自身のレキシカルスコープを越えてそれらの値を捕捉する。

(defn messenger-builder [greeting]
  (fn [who] (println greeting who))) ; closes over greeting greetingを閉包する

;; greeting provided here, then goes out of scope greetingの値はここで与えられてスコープを離れる
(def hello-er (messenger-builder "Hello"))

;; greeting value still available because hello-er is a closure hello-rはクロージャなのでgreetingの値はここで使える
(hello-er "world!")
;; Hello world!

Java Interop

Invoking Java code Javaコードを呼び出す

Below is a summary of calling conventions for calling into Java from Clojure: ClojureからJavaを呼び出す場合の慣用的方法を以下に示す。

Task 課題 Java Clojure
Instantiation インスタンス生成 new Widget("foo") (Widget. "foo")
Instance method インスタンスメソッド rnd.nextInt() (.nextInt rnd)
Instance field インスタンスフィールド object.field (.-field object)
Static method スタティックメソッド Math.sqrt(25) (Math/sqrt 25)
Static field スタティックフィールド Math.PI Math/PI

Java Methods vs Functions Javaのメソッドと関数との違い

  • Java methods are not Clojure functions JavaのメソッドはClojureの関数ではない
  • Can't store them or pass them as arguments それを保持することも、引数に渡すこともできない
  • Can wrap them in functions when necessary 必要なら関数でラップできる
;; make a function to invoke .length on arg 引数に対して.lengthを呼び出す関数を作る
(fn [obj] (.length obj))

;; same thing 同じ意味
#(.length %)

Test your knowledge 理解の確認

1) Define a function greet that takes no arguments and prints "Hello". 引数を取らない関数greetを作成し「Hello」と印字せよ。

2) Redefine greet using def, first with the fn special form and then with the #() reader macro. defを使ってgreetを定義し直してください。1度目は特殊形式fnを用いて、2度目はリーダマクロ#()を使ってください。

;; using fn fnを使う
(def greet __)

;; using #() #()を使う
(def greet __)

3) Define a function greeting witch: 以下の要件を満たす関数greetingを定義しなさい。

  • Given no arguments, returns "Hello, World!" 引数が0個だったらこう返す
  • Given one argument x, returns "Hello, x!" 引数が1個だったらこう返す
  • Given two arguments x and y, returns "x, y!" 引数が2個だったらこう返す
;; Hint use the str function to concatenate strings ヒント。文字列の連結にはstr関数が便利だ。
(doc str)

(defn greeting ___)

;; For testing テスト用
(assert (= "Hello, World!" (greeting)))
(assert (= "Hello, Clojure!" (greeting "Clojure")))
(assert (= "Good morning, Clojure!" (greeting "Good morning" "Clojure")))

4) Define a function do-nothing which takes a single argument x and returns it, unchanged. 唯一の引数xを取り、変更せずそのまま返す関数do-nothingを定義しなさい

(defn do-nothing [x] ___)

In Clojure this is the identity function. これはClojureにおけるidentity関数だ。By itself, identity is not very useful, but it is sometimes necessary when working with higher-order functions. これ自身は特別有用ではないが、高階関数を使う時にしばしば必要になる。

(source identity)

5) Define a function always-thing which takes any number of arguments, ignores all of them, and returns the keyword :thing. 任意個の引数を取ってそれらを無視し、キーワード:thingを返す関数always-thingを定義しなさい。

(defn always-thing [__] ___)

6) Define a function make-thingy which takes a single argument x. 唯一の引数xを取る関数make-thingyを定義してください。It should return another function, which takes any number of arguments and always returns x. その関数は、任意個の引数を取って常にxを返す別の関数を返すものとします。

(defn make-thingy [x] ___)

;; Tests テスト
(let [n (rand-int Integer/MAX_VALUE)
      f (make-thingy n)]
  (assert (= n (f)))
  (assert (= n (f :foo)))
  (assert (= n (apply f :foo (range)))))

In Clojure, this is the constantly function. これはClojureconstantly関数だ。

7) Define a function triplicate which takes another function and calls it three times, without any arguments. 関数を引数に取って、引数を与えずにその関数を3回呼び出す関数triplicateを定義しなさい。

(defn triplicate [f] ___)

8) Define a function opposite which takes a single argument f. 唯一の引数fを取る関数oppositeを定義してください。It should return another function which takes any number of arguments, applies f on them, and then calls not on the result. その関数は、任意個の引数と取ってそれらにfを適用し、その結果にnotを呼び出す関数を返すものとします。The not function in Clojure does logical negation. Clojurenot関数は論理否定を行います。

(defn opposite [f]
  (fn [& args] ___))

In Clojure, this is the complement function. これはClojureにおけるcomplement関数の動作です。

(defn complement
  "Takes a fn f and returns a fn that takes the same arguments as f,
  has the same effects, if any, and returns the opposite truth value.
  関数fを取りfと同じ引数を取る関数を返す。fに効果があれば同じく効果する。
  そして逆の真義値を返す。"
  [f]
  (fn
    ([] (not (f)))
    ([x] (not (f x)))
    ([x y] (not (f x y)))
    ([x y & zs] (not (apply f x y zs)))))

9) Define a function triplicate2 which takes another function and any number of arguments, then calls that functoin three times on those arguments. 1つの関数と任意個の引数を取り、それら引数でその関数を3回呼び出す関数triplicate2を定義しなさい。Re-use the function you defined in the earlier triplicate exercise. 上で定義した関数triplicateを再利用しなさい。

(defn triplicate2 [f & args]
  (triplicate ___))

10) Using the java.lang.Math class (Math/pow, Math/cos, Math/sin, Math/PI), demonstrate the following mathematical facts: java.lang.Mathに定義されたこれらの関数を用いて、以下の数学的事実を確認しなさい。

  • The cosine of pi is -1 円周率の余弦は-1だ
  • For some x, sin(x)2 + cos(x)2 = 1 あるxが存在してこの式が成り立つ

11) Define a function that takes an HTTP URL as a string, fetches that URL from the web, and returns the content as a string. HTTP URLを文字列として取り、そのURLにあるデータをウェブから取得し、内容を文字列で返す関数を定義しなさい。

HInt: Using the java.net.URL class and its openStream method. java.net.URLのopenStreamメソッドを使うとよい。Then use the Clojure slurp function to get the content as a string. そうしてClojureslurp関数を使えば内容を文字列として得られる。

(defn http-get [url]
  ___)

(assert (.contains (http-get "http://www.w3.org") "html"))

In fact, the Clojure slurp function interprets its argument as a URL first before trying it as a file name. 実のところ、Clojureslurp関数は、引数をファイル名として解釈することを試みる前に、URLとして解釈しようとする。Write a simplified http-get: シンプルにしたhttp-getを書いてください。

(defn http-get [url]
  ___)

12) Define a function one-less-arg that takes two arguments: 次の2つの引数を取る関数one-less-orgを定義しなさい。

  • f, a function 関数
  • x, a value

and returns another function which calls f on x plus any additional arguments. そして、xに加えて任意個の引数に対してfを適用する関数を返すものとします。

(defn one-less-arg [f x]
  (fn [& args] ___))

In Clojure, the partial function is a more general version of this. これを一般化したものがClojurepartial関数です。

13) Define a functoin two-fns which takes two functions as arguments, f and g. 関数fgを引数に取る関数two-fnsを定義しなさい。It returns another function which takes one argument, calls g on it, then calls f on the result, and returns that. この関数は、1つの引数を取ってgを適用し、その結果にさらにfを適用した結果を返すものとします。

That is, your function returns the composition of f and g. これはつまり、あなたが実装する関数は、fgの合成を返すということです。

(defn two-fns [f g]
  ___)

原文: https://clojure.org/guides/learn/functions