よろずや

Ruby メモ

目次

Ruby を勉強する

Lisp と比較していってみる.

配列

配列は簡単.

irb(main):043:0> [1, 2, 3].length 
=> 3

Ruby

* (length #(1 2 3))
3

Common Lisp

ハッシュ

irb(main):045:0> {"l" => "lisp", "r" => "ruby", "p" => "python" }["l"]
=> "lisp"

Ruby

* (setf hash (make-hash-table :test #'equal))
#<EQUAL hash table, 0 entries {48E31D4D}>
* (setf (gethash "r" hash) "ruby")
"ruby"
* (setf (gethash "l" hash) "lisp")
"lisp"
* (setf (gethash "p" hash) "python")
"python"
* (gethash "l" hash)
"lisp"
t
*

Common Lisp

最初から値を持ったハッシュを宣言してつかいたいとき,Lisp では手間がかかりすぎるように見える.

↓のようなマクロをつかえば,

(defmacro hash (entry &key (test #'equal) (size 64) (rehash-size 1.5))
  (let ((h (gensym))
	(e (gensym)))
    `(let ((,h (make-hash-table :test ,test :size ,size :rehash-size ,rehash-size)))
      (dolist (,e ,entry)
	(setf (gethash (first ,e) ,h) (second ,e)))
      ,h)))

Common Lisp

(hash '((key value) (key value) ...)) のように書ける.これを使えば,

* (gethash "l" (hash '(("l" "lisp") ("r" "ruby") ("p" "python"))))
"lisp"
t

Common Lisp

という感じに.

if

irb(main):052:0> x = 0
=> 0
irb(main):053:0> if x == 1
irb(main):054:1>   puts "one"
irb(main):055:1> elseif x==2
irb(main):056:1>   puts "two"
irb(main):057:1> else
irb(main):058:1*   puts "other"
irb(main):059:1> end
other
=> nil

Ruby

* (setf x 0)
0
* (if (= x 1)
    (write-line "one")
    (if (= x 2)
      (write-line "two")
      (write-line "other")))
other
"other"

Common Lisp

あるいは cond 使って

(cond ((= x 1) (write-line "one"))
      ((= x 2) (write-line "two"))
      (t       (write-line "other")))
とか
(write-line
  (cond ((= x 1) "one")
        ((= x 2) "two")
        (t       "other")))

Common Lisp

while

irb(main):009:0> sum=0
=> 0
irb(main):010:0> i=1
=> 1
irb(main):011:0> while i <= 10
irb(main):012:1>   sum+=i
irb(main):013:1>   i+=1
irb(main):014:1> end
=> nil
irb(main):015:0> sum
=> 55

Ruby

lisp には while という構文は標準では存在しない.繰りかえし構文の do を使うと

* (do ((i   0 (1+ i))
       (sum 0 (+ i sum)))
      ((> i 10) sum))
55

Common Lisp

とかけるが,やはりマクロで while 構文を定義してみてもいい.

(defmacro while (condition &body body)
  `(do ()
       ((not ,condition))
    ,@body))

Common Lisp

これを使って

* (setf sum 0 i 1)
1
* (while (< i 10)
    (setf sum (+ i sum))
    (incf i))
nil
* sum
55

Common Lisp

とできた.

x = x*x while x < 100

Ruby

(while (< x 100) (setf x (* x x)))

Common Lisp

正規表現

irb(main):004:0> "This is LANGUAGE".sub(/L.*/, "Ruby")
=> "This is Ruby"

Ruby

;; cl-awk が必要
* (sub "L.*" "Lisp" "This is LANGUAGE")
"This is Lisp"

Common Lisp

yield

irb(main):010:0> def call_2_times
irb(main):011:1>   yield
irb(main):012:1>   yield
irb(main):013:1> end
=> nil
irb(main):014:0> call_2_times { puts "Hello, World" }
Hello, World
Hello, World
=> nil

Ruby

* (defun call-2-times (f)
    (funcall f)
    (funcall f))
call-2-times
* (call-2-times (lambda () (write-line "Hello, World")))
Hello, World
Hello, World

Common Lisp

each

irb(main):023:0> ["1", "2", "3"].each { |x| puts x}
1
2
3
=> ["1", "2", "3"]

Ruby

(defmacro each (seq fun)
  (let ((sequence seq))
    `(do ((len (length ,sequence))
	  (index 0 (1+ index)))
      ((= index len) ,sequence)
      (funcall ,fun (elt ,sequence index)))))

Common Lisp

* (each #("1" "2" "3") #'(lambda (x) (write-line x)))
1
2
3
#("1" "2" "3")

Commmon Lisp

Ruby と elisp

List Comprehension - defmacro によるリストの内包表記 で作った list-of マクロ が elisp でも使えた.せっかくなのでそれを使った例も混ぜて比較.

ただ,upto は elisp だと再帰の深さの限界があるので,↓のように変更する.

(defun upto (from to)
  (let ((acc nil))
    (dotimes (i (1+ (- to from)) (nreverse acc))
      (push (+ i from) acc))))

配列(リスト)への操作

collect

(defun collect (fn lst)
  (let ((acc nil))
    (dolist (e lst (nreverse acc))
      (push (funcall fn e) acc))))

;; ["a", "b", "c"].collect {|x| x+"!"}
(collect (lambda (x) (concat x "!")) '("a" "b" "c"))
(mapcar (lambda (x) (concat x "!")) '("a" "b" "c"))

;; list-of マクロを使うと
(list-of (concat x "!") (x in '("a" "b" "c")))

detect

(defun detect (fn lst)
  (dolist (e lst)
    (when (funcall fn e) (return e))))
;; (1 .. 100).detect { |i| i%7==0 and i%5==0 }
(detect #'(lambda (i) (and (= (% i 5) 0) (= (% i 7) 0))) (upto 1 100))

;; list-of の条件部分が and なのを利用すると,
(first
  (list-of i (i in (upto 1 100))
             (= (% i 7) 0)
             (= (% i 5) 0)))

find_all

(defun find-all (fn lst)
  (let ((acc nil))
    (dolist (e lst (nreverse acc))
      (when (funcall fn e)
	(push e acc)))))

;; [1, 2, 3].find_all {|i| i%2==1}
(find-all #'oddp '(1 2 3))
(find-all (lambda (i) (= (% i 2) 1)) '(1 2 3))
(loop for x in '(1 2 3) when (lambda (i) (= (% i 2) 1)) collect x)

;; list-of マクロを使うと
(list-of x (x in '(1 2 3)) (= (% x 2) 1))