Clojure を学ぶ 公式サイト編2

2020-06-08

以前、ちょっとだけかじった事があった Clojure を、もういっかい勉強中。

公式な日本語ドキュメントもあるけど、自分なりにまとめていきます。

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

関数の宣言

; 定義
;;    名前   引数           本体
;;    -----  ------  -------------------
(defn greet  [name]  (str "Hello, " name) )
;
; 呼び出し
(greet "world")
=> "Hello, world"
;
; 引数の数毎に関数を定義できる
(defn messenger
  ([]     (messenger "Hello world!"))
  ([msg]  (println msg)))
(messenger)
=> Hello world!
   nil
(messenger "Hi there!")
=> Hi there!
   nil
;
; 可変引数は、引数リストの最後に「&」の後ろ。
(defn hello [greeting & who]
  (println greeting who))
;可変引数はリストで入っている
(hello "Hello" "Nancy")
=> Hello (Nancy)
   nil
(hello "Hello" "Nancy" "Masao")
=> Hello (Nancy Masao)
   nil
;
; 無名関数   
((fn [message] (println message))  "Hello world!" )
; 無名関数の省略形
#(+ 6 %) ;(fn [a] (+ 6 a))
#(+ %1 %2) ;(fn [a b] (+ a b ))
#(println %1 %2 %&) ;(fn [a b & cs] (println a b cs))  可変引数
; 引数をVectorにして返す関数を省略形で書く場合
(#([%]) 1) ;NG ([x] ([x]))こう展開されてエラーになる
(#(vector %) 1) ;ok
((fn [x] [x]) 1) ;ok
(vector 1) ;これがシンプル
;
;defn = def + fn
(defn greet [name] (str "Hello, " name))
(def greet (fn [name] (str "Hello, " name)))

apply

(apply f '(1 2 3 4))    
(apply f 1 '(2 3 4))    
(apply f 1 2 '(3 4))    
(apply f 1 2 3 '(4))  ;全て(f 1 2 3 4)と同じ
;
(defn plot [shape coords]   ;coords = [x y]
  (plotxy shape (first coords) (second coords)))
;↓こう書ける
(defn plot [shape coords]
  (apply plotxy shape coords)) 

let

;let で定義された名前は、外部コンテキスト内の名前よりも優先
(let [A 2]
  (let [A 1]
    (println A))
  (println A))
(println A)
=> 1
   2
   Unable to resolve symbol: A in this context

Closure

(defn messenger-builder [greeting]
  (fn [who] (println greeting who))) 
;
(def hello-er (messenger-builder "Hello"))
;
;; greetingはスコープ外だが、hello-er はクロージャなので、"Hello"は有効
(hello-er "world!")
=>  Hello world!

Java 相互運用

;
new Widget("foo"); //java
(Widget. "foo") ;Clojure
;
rnd.nextInt();
(.nextInt rnd)
;
object.field;
(.-field object)
;
Math.sqrt(25);
(Math/sqrt 25)
;;
Math.PI;
Math/PI
;
;;javaのメソッドは、Clojureの関数ではないので、
;;束縛したり引数として渡せない。
;;必要に応じてラップできる
(fn [obj] (.length obj))
#(.length %)

確認問題

:; 1. 引数を取らず、"Hello "を表示する関数greetを定義せよ
(defn greet [] (println "Hello"))
;
;; 2. 最初に fn 特殊形式を使用し、次に #() リーダーマクロを使用して、def を使用して greet を再定義せよ。
(fn []  (println "Hello"))
#(println "Hello")
(def greet (fn [] (println "Hello"))
;
;; 3. グリーティング関数を定義します。
;  引数がない場合、"Hello, World!"を返します。
;  引数 x が 1 つ与えられると、"Hello, x!"
;  2つの引数xとyが与えられると、"x, y!"
(defn greeting
   [] "Hello, World!"
   [x] (str "Hello, " x "!")
   [x y] (str x ", " y "!"))
;
;; 4. 単一の引数xを取り、変更せずに返す関数do-nothingを定義せよ
(defn do-nothing [x] x)
;
;; 5. 任意の数の引数を取り、それらのすべてを無視して、数値100を返す関数always-thingを定義せよ
(defn always-thing [& xs] 100)
;
;; 6. 単一の引数xを取る関数make-thingyを定義せよ。これは、任意の数の引数を取り、常にxを返す別の関数を返す必要があります。
(defn make-thingy [x] 
  (fn [& xs] x))
;
;; 7. 別の関数を取り、引数なしで3回呼び出す関数triplicateを3つ定義せよ
(defn triplicate [f]
  (f)(f)(f))
;
;; 8. 単一の引数fを取る関数opposite を定義せよ。これは、任意の数の引数を取り、fをそれらに適用し、結果を呼び出さない別の関数を返す必要があります。
(defn opposite [f]
  (fn [& args] (map f args)))
;
;; 9. 別の関数と任意の数の引数を取り、それらの引数に対してその関数を3回呼び出す関数triplicate2を定義せよ。
;     前に定義したtriplicate関数を再利用。
(defn triplicate2 [f & args]
  (triplicate (partial f args))
;
;; 10. java.lang.Mathクラスを使用して、以下の数学的な事実を示せ。
;PIのコサインは-1
(==
  -1
  (Math/cos (Math/PI)))
=> true
;sin(x)^2 + cos(x)^2 = 1
((fn [x] 
  (== 1
    (+ 
      (Math/pow (Math/sin x) 2) 
      (Math/pow (Math/cos x) 2)))) 
2)
=> true
;