逆引きclojure文字列の部分(途中)

合っているのか自信がないのでblogに貼るだけにしておく

今のところ

文字のリスト(sequence)から文字列を作成する上手い方法が見つかっていない。
暫定的な対応策は以下

(defn cs-to-str [cs]
  (String. #^"[C" (into-array Character/TYPE cs)))

文字列を結合する

(str "foo" "bar") ; => "foobar"

大文字・小文字に揃える

(. "Hi, Clojurian" toUpperCase) ; => "HI, CLOJURIAN"
(. "Hi, Clojurian" toLowerCase) ; => "hi, clojurian"

大文字と小文字を入れ替える

(defn swap-case [str]
  (cs-to-str
   (map #(cond (Character/isUpperCase %) (Character/toLowerCase %)
	       :else (Character/toUpperCase %))
	str)))

(swap-case "Hi, Clojurian") ; => "hI, cLOJURIAN"

複数行の文字列を作成する

"abc\ndef\nghi"

部分文字列を取り出す

(subs "0123456" 2 5) ; => "234"

文字列を一文字づつ処理する

(doseq [c "abc"] (pr c))

文字列を一行づつ処理する

(use '[clojure.contrib.duck-streams :only (read-lines)])

(doseq [line (read-lines (java.io.StringReader. "abc\ndef\nghi"))]
  (println line))

文字列の先頭・末尾の空白を削除する

(cs-to-str (drop-while #(= \space %)  "  abc"))
(cs-to-str (reverse (drop-while #(= \space %) (reverse "abc   "))))

文字列を数値に変換する

(Integer/parseInt "0123") ; => 123

文字とUnicodeスカラー値を相互変換する

(char (int \a)) ; => \a

文字列を URI エンコードする

(java.net.URLEncoder/encode "逆引き Clojure") ; => ; => "%E9%80%86%E5%BC%95%E3%81%8D+Clojure"
(java.net.URLDecoder/decode  "%E9%80%86%E5%BC%95%E3%81%8D+Clojure") ; => "逆引き Clojure"

文字列を Base64 エンコードする

文字列のエンコーディングを変換する

文字列を分割する

練習問題 - プログラミングスレまとめ in VIPをやってみる

全体的にlazy-seqをたくさん使っているみたい。

FizzBuzz

無限ストリーム(sequence)なこと以外特筆することはなさそう。

(def fizzbuzzs
     (for [i (iterate inc 1)]
       (cond (= 0 (rem i 3) (rem i 5)) "fizzbuzz"
	     (= 0 (rem i 3)) "fizz"
	     (= 0 (rem i 5)) "buzz"
	     :else i)))

;;(take 10 fizzbuzzs) ; => (1 2 "fizz" 4 "buzz" ...)

素数判定

  • sqrtがないのでjavaのものを使う(Math/sqrt)
(defn prime? [n]
  (let [sqn (Math/sqrt n)]
    (if (or (>= 2 n) (zero? (rem n 2)))
      false
      (every? #((not (zero? (rem n %))))
	      (take-while #(> sqn %) (iterate (comp inc inc) 3))))))
;;((interleave (range  1 10) (map prime? (range 1 10)))

平方根を求めてみる。

  • newton法
  • absがないので自分で作る
;;f(x)=x^2 = n; x^2 - n = 0;
;;f'(x)=2x
;;(y=0)-f(x0) = f'(x0) * (x-x0)
;;-f(x0)/f'(x0) = x - x0
;;x = x0 - f(x0)/f'(x0)
;;x = x0 - (x0^2-n)/2x0
;;x = (x0+(n/x0))/2
(def *tolerance* 0.001)
(defn abs [i] (if (neg? i) (- i) i))
(defn my-sqrt [n]
  (loop [i 1.0]
    (let [i* (/ (+ i (/ n i)) 2.0)]
      (cond (< (abs (- i i*)) *tolerance*) i*
	    :else (recur i*)))))

leap-year?

(defn leap-year? [y]
  (letfn [(dividable? [n] (zero? (rem y n)))]
    (and (dividable? 4)
    	 (or (not (dividable? 100))
	     (dividable? 400)))))

hanoi

ダメ過ぎる。あとで修正しないと。

(defn hanoi [n a b c]
  (fn [fun]
    (letfn [(rec
	     [n from to tmp]
	     (if (= n 1)
	       (fun n from to)
	       (do (rec (dec n) from tmp to)
		   (fun n from to)
		   (rec (dec n) tmp to from))))]
      (rec n a b c))))
;; ((hanoi 5 'A 'B 'C) 
;;  (fn [n from to] (println n ":" from "->" to)))

転置行列を作成

  • costが分かっていない
  • vectorにしておく必要があるのか謎
(defn transpose [coll]
  (apply map (fn [& xs] (apply vector xs)) coll))
(defn transpose* [coll]
  (apply vector (transpose coll)))

;; (transpose [[1 2 3] [4 5 6] [7 8 9]])
;; (transpose* [[1 2 3] [4 5 6] [7 8 9]])

LCG

  • sequenceにしていることくらいかも
;; X_{n+1} = \left( A \times X_n + B \right) \bmod M 
;; M=65536(=2^16),A=997,B=1,Xの初期値を12345として100個の乱数を発生させ,その値と平均を出力しなさい。
(defn lcg [m a b x]
  ((fn step [x]
     (lazy-seq
      (let [x* (rem (+ (* a x) b) m)]
	(cons x* (step x*)))))
   x))

;; (defn ave [coll]
;;   (/ (reduce + coll) (count coll)))

;; (let [m 65536
;;       xs (take 100 (map #(/ (float %) m) (lcg m 997 1 12345)))]
;;   (doseq [x xs] (print x ","))
;;   (println "\nave:" (ave xs)))

数当てゲーム

  • 数値を標準入力から上手く取る方法が分からない
  • 例外をcatchした先ではrecurが使えない
    • そんなに何度もミスることがないと考えれば普通に再帰しても良いかも
(defn get-input [] 
  (try (Integer/parseInt (read-line))
       ;;Cannot recur from catch/finally
       (catch java.lang.NumberFormatException _ (get-input))))

(defn quiz [n]
  (loop []
    (let [m (get-input)]
      (cond (= n m) n
	    (< n m) (do (println "high") (recur))
	    :else (do (println "low") (recur))))))
;;(quiz 10)

hit&blow

整数をcollectionにする方法が良く分からない。

  • とりあえずunfoldを作ってその上にnum-to-collを作成した
  • filterは複数のcollectionを取れない
    • zipmapで合体させてから利用
  • srfi-1のfindのような関数が見つからない
    • someで代用
(defn gen-n [k]
  (assert (and (pos? k) (< k 9)))
  (take k (sort-by (fn [_] (rand)) (range 1 10))))

(defn unfold [p f g seed]
  ((fn step [x]
     (lazy-seq (when-not (p x) (cons (f x) (step (g x))))))
   seed))

(defn num-to-coll [num]
  (reverse (unfold zero? #(rem % 10) #(quot % 10) num)))

(defn hit&blow 
  ([ans] ;;ans is coll
     (let [ans (if (number? ans) (num-to-coll ans) ans)
	   len (count ans)]
       (loop []
	 (let [ns (num-to-coll (get-input))
	       hits (count (filter (fn [[k v]] (= k v)) (zipmap ns ans)))
	       blows (count (filter (fn [i] (some #{i} ans)) ns))]
	   (cond (= hits len) ns
		 :else (do (println "hits:" hits "blows:" blows)
			   (recur)))))))
  ([] (hit&blow (gen-n 4))))

カレンダー出力

  • java.util.Calendarを使えば良いらしい。
  • dotoの使い方が分かってきた。
  • 関数の名前の付け方がおかしい。
    • 特にdump-calendar
(import 'java.util.Calendar)

(defn calendar [y m d]
  (doto (Calendar/getInstance)
    (. clear) 
    (. set y (dec m) d)))

(defn calendar-wday [cal]
  (. cal get (Calendar/DAY_OF_WEEK)))

(defn calendar-last-date [cal]
  (let [cal* (doto (. cal clone)
	       (. add Calendar/MONTH 1)
	       (. add Calendar/DATE -1))]
    (. cal* get Calendar/DATE)))

(defn slices [n coll]
  ((fn step [coll]
     (lazy-seq 
      (let [[items rest] (split-at n coll)]
	(cons items (step rest)))))
   coll))
;(slices 2 (iterate inc 1)) ; => ((1 2) (3 4) (5 6) (7 8) (9 10) ...)

(defn calendar-to-seq [y m]
  (let [cal (calendar y m 1)
	wday (dec (calendar-wday cal))
	last-date (calendar-last-date cal)
	[first-week rest-week]
	(split-at (- 7 wday) (take-while #(<= % last-date) (iterate inc 1)))]
    (cons (concat (replicate wday "   ") first-week)
	  (slices 7 rest-week))))

(defn dump-calendar [y m]
  (doseq [week (take-while (complement empty?) (calendar-to-seq y m)]
	  (doseq [day week] 
	    (if (string? day) (print day) (printf "%3d" day)))
	  (newline)))

;;(dump-calendar 2010 2)

配列いじり?

配列の先頭はそのままに、先頭以外の要素をすべて0に置き換える。
飽きた。

(defn replace-tail [coll]
  (cons (first coll) (repeat 0)))