コンテンツにスキップ

Clojure

インストール

Windows

このページ参照

Linux

このページ参照.

1
2
sudo apt install bash curl rlwrap openjdk-11-jdk
cd /tmp

インストールスクリプトは公式にアクセスして確認した上で最新バージョンを使おう.

cider

ツール類

公式ページを参考に~/binleinまたはlein.batを置く.

ガイド: 参考リンク集

ガイド: 関数プログラミングの学習のために

Edabit has lots and lots of coding exercises. They're small. They're clearly explained. They are graded Very Easy to Expert. Use these challenges as a way to practice your functional programming skills. See if you can solve the same problem in different ways by applying different skills.

Project Euler has amassed many programming challenges. They are often very mathematical, but everything is clearly explained. The great thing about these challenges is that they will force you to face real limits. For instance, figuring out the first 10 prime num-bers is easy. Figuring out the 1,000,000th prime number before the sun dies out is a real challenge! You'll face memory limitations, performance limitations, stack size limitations, and more. All of these limits will force you to make your skills practical and not just theoretical.

CodeWars has a large collection of exercises that are challenging enough to test your skills but small enough to solve in a few minutes. These are great for practicing different skills on the same problem.

Code Katas are a kind of practice where you solve the same problem multiple times. We perform a kata more for practicing the process of programming than for solving some challenge. These are also good because they let you integrate your new functional program-ming skills with your other development skills, such as testing.

2022-05時点でのWeb開発

Pedestal + Duct構成ならこんな感じかな(PedestalとDuctで主要機能をまかなえる)。

  • HTTP抽象: Pedestal
  • ルーティング: Pedestal
  • 入力バリデーション: malli
  • RDBアクセス: next.jdbc + Honey SQL
  • ライフサイクル管理: Duct (Integrant)
  • 設定: Duct
  • clojure.spec強化: Orchestra, Expound

Leiningen: GitHubのレポジトリを指定する

  • Clojure CLI (deps.edn) でも対応できるらしい.
  • 参考
  • profiles.cljに次のように追記
    • {:user {:plugins [[lein-git-deps "0.0.2-SNAPSHOT"]]}}
  • project.clj に次のように追記(:dependenciesと同じインデントになるように)
    • :git-dependencies [["https://github.com/tobyhede/monger.git"]]

MySQL接続

  • cf.
  • MySQLでtodo_clj_devを作っておくこと: CREATE DATABASE todo_clj_dev
  • 仮定: dbuser=root, dbpass=""
    • あとでの設定で効いてくる: 必要に応じて書き換える

project.clj

1
2
3
4
5
6
7
8
9
(defproject todo-clj "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [org.clojure/clojure-contrib "1.2.0"]
                 [org.clojure/java.jdbc "0.7.12"]
                 [mysql/mysql-connector-java "8.0.29"]])

sample.clj

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
(ns todo-clj.sample
  (:require [hiccup.core :as hc]
            [hiccup.page :as hp]
            [clojure.java.jdbc :as jdbc]
            [clojure.test :refer :all]))
(require '[clojure.java.jdbc :as jdbc])

(def db-spec {:dbtype "mysql" :dbname "todo_clj_dev" :host "localhost" :port 3306 :user "root" :password ""})
(jdbc/db-do-commands
 db-spec
 (jdbc/drop-table-ddl :member))
(jdbc/db-do-commands
 db-spec
 (jdbc/create-table-ddl :member
                        [[:id :int :primary :key "AUTO_INCREMENT"] [:name "varchar(20)" "not null"] [:age :int] [:address "varchar(100)"]]
                        {:table-spec "ENGINE=InnoDB" :entities clojure.string/lower-case}))
(do
  (jdbc/insert! db-spec :member {:name "phasetr"})
  (jdbc/insert! db-spec :member {:name "foo"})
  (jdbc/insert-multi! db-spec :member [{:name "alice" :address "wonderland"} {:name "cheshire"} {:name "mad hatter"}]))

REPLで役立つ関数

  • 参考
    • doc, find-doc, apropos, dir, source, pst

REPLを再起動せずにリフレッシュする

  • 参考
  • projece.clj:dependencies [[org.clojure/tools.namespace "1.1.0"] ]を指定する.
  • lein depsを実行してインストールする.
  • REPL内で次のコマンドを実行する.
1
(do (use '[clojure.tools.namespace.repl :only (refresh)]) (refresh))

印字・print

四種類ある.

人間が読む 機械が読む
改行あり println prn
改行なし print pr

関数に引数をリストで渡す: Schemeとの比較

Schemeの場合.

1
2
3
4
5
6
gosh> ((lambda x (print x)) "foo")
(foo)
#<undef>

gosh> ((lambda (x) (print x)) "foo")
foo

Clojureで括弧なしでリストで渡す方法.

1
(defn [& args] (println args))

関数・マクロのリファレンス

#?@$

  • 参考
  • 条件つきスプライシングリーダーマクロ.
1
2
3
(defn build-list []
  (list #?@(:clj  [5 6 7 8]
            :cljs [1 2 3 4])))

defmulti

  • doc
  • (defmulti name docstring? attr-map? dispatch-fn & options)

defmethod

  • doc
  • (defmethod multifn dispatch-val & fn-tail)
1

juxt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
;; Extract values from a map, treating keywords as functions.
((juxt :a :b) {:a 1 :b 2 :c 3 :d 4})
;;=> [1 2]

;; sort list of maps by multiple values
(sort-by (juxt :a :b) [{:a 1 :b 3} {:a 1 :b 2} {:a 2 :b 1}])
;;=> [{:a 1 :b 2} {:a 1 :b 3} {:a 2 :b 1}]

((juxt + * min max) 3 4 6)
;;=> [13 72 3 6]

;; split a sequence into two parts
user=> ((juxt take drop) 3 [1 2 3 4 5 6])
;; => [(1 2 3) (4 5 6)]

sort-by

1
2
3
4
5
6
7
8
user=> (sort-by count ["aaa" "bb" "c"])
("c" "bb" "aaa")

user=> (sort-by first [[1 2] [2 2] [2 3]])
([1 2] [2 2] [2 3])

user=> (sort-by first > [[1 2] [2 2] [2 3]])
([2 2] [2 3] [1 2])

簡単コメント

  • セミコロンによるコメントアウト以外のコメントアウト法がある.
  • #_をフォームまたはシンボルの前につけると, そのフォームまたはシンボルがコメントアウトされる.

記号へのリファレンス

  • [参考ページ(https://japan-clojurians.github.io/clojure-site-ja/guides/weird_characters){target=_blank}

作ったファイルを別のファイルにインポートする

下のように(ns)(:gen-class)を書いておく.

1
2
3
(ns fdg.prologue
  (:gen-class)
  (:require [sicmutils.env :as e :refer :all]))

この状態でこのフォームを評価して, インポートしたいファイルでrequireなり何なりすればよい.

リストの処理: doseq, for

  • 参考
  • doseqnilを返す: prnなど副作用発生が前提
  • forは値を返してくれる.
1
2
3
4
5
6
7
(doseq [letter [:a :b]
        number (range 3)] ; 0, 1, 2の要素を持つリスト
  (prn [letter number]))

(for [letter [:a :b]
      number (range 3)] ; 0, 1, 2の要素を持つリスト
  [letter number])