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. この例で、println
がwho
を印字した有様から、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
. defn
はdef
とfn
を縮めたものだと考えるといいかもしれない。The fn
defines 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つのローカル束縛x
とy
を作り出す。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
が、a
、b
、c
を定義する新しいスコープを作っている。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. これはClojureのconstantly
関数だ。
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. Clojureのnot
関数は論理否定を行います。
(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に定義されたこれらの関数を用いて、以下の数学的事実を確認しなさい。
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. そうしてClojureのslurp
関数を使えば内容を文字列として得られる。
(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. 実のところ、Clojureのslurp
関数は、引数をファイル名として解釈することを試みる前に、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. これを一般化したものがClojureのpartial
関数です。
13) Define a functoin two-fns
which takes two functions as arguments, f
and g
. 関数f
とg
を引数に取る関数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
. これはつまり、あなたが実装する関数は、f
とg
の合成を返すということです。
(defn two-fns [f g] ___)