10 Scheme One Liners to Impress Your Friends

http://d.hatena.ne.jp/y2q_actionman/20110607/p1 のパクりを(n)moshで。

読み方

(import ...)はライブラリの読み込みでプログラム本体では無いです。(Rubyのrequireのような。)
SRFI-xというのは、JavaのJSRとかPythonのPEPのようなもので、番号の付いた言語仕様。moshにもいくつかのメジャーなSRFIが収録されています。
moshは今のところ、起動直後では殆ど何もできません。このへんはhigeponと意見の合ってないところで、個人的にはココに挙げるような手続きは全部preloadしちゃって良いと思っていたりします(REPL上では)。

1. Multiple Each Item in a List by 2

SRFI-42のlist-ecを使うのが一番単純。

(import (srfi :42))
(list-ec (: n 1 11) (* n 2))

もちろん出題意図通り解くならmapでできる。mapはScheme標準で入っているが、iotaはSRFI-1に収録されている。(iota 10 1)でリスト(1 2 3 4 5 6 7 8 9 10)の意味。

(import (srfi :1))
(map (^n (* n 2)) (iota 10 1))

mapの中の^nはGaucheから借りてきた表記で、(lambda (n) ...)の意味。moshではこの表記は(shorten)ライブラリに収録されている。

2. Sum a List of Numbers

SRFI-42にsum-ecがあるのでそれで一発。

(import (srfi :42))
(sum-ec (: n 1000) n)

問題の書き方の通りに解くならapplyを使う。

(apply + (iota 1000))

3. Verify if Exists in a String

(import (srfi :13) (srfi :1) (srfi :26))
(define words '("scala" "akka" "play framework" "sbt" "typesafe"))

(any (^e (any (cut string=? <> e) words))
     (string-tokenize "This is an example tweet talking about scala and sbt."))

うーむ複雑だ。。文字列を空白で区切るstring-tokenizeはSRFI-13、anyはSRFI-1、anyの中で使っているcutはSRFI-26。

4. Read in a File

(import (yuni util files))
(file->string-list "data.txt")

ライブラリ(yuni util files)にfile→string-list手続きを入れてある。実はmosh自体にもこの手続きがあるが、命名に一貫性が無いのであまりオススメしない。

5. Happy Birthday to You!

(import (srfi :48) (srfi :42))
(do-ec (: n 4) (format #t "Happy Birthday ~a\n" (if (= n 2) "dear Tony" "to You")))

単純な繰り返し実行はSRFI-42のdo-ec、printfのようなフォーマット出力はSRFI-48のformat。
moshにはネイティブのformatも有るが、移植性が無いのであまりオススメしない。

6. Filter list of numbers

Scheme標準の手続きとしてpartitionが有るのでこれを使う。

(import (srfi :26))
(partition (cut > <> 60) '(49 58 76 82 88 90))

partitionは普通の手続きとちがって2つの値を返す。なので、実際に値を使うときはlet-valuesやSRFI-8のreceiveを使う必要がある。

7. Fetch and Parse an XML web service

(import (yuni lib ssax parsing) (http) (srfi :8))
(receive (str status headers) (http-get->utf8 "http://search.twitter.com/search.atom?&q=scala")
   (ssax:xml->sxml (open-string-input-port str) '()))

実はmoshにはこっそりSSAXが入っている。なので、XMLを普通のS式に変換して取り扱うことができる。
http経由でURLを取得するにはhttpライブラリを使う。http-get→utf8という名前だが、実際には文字列を返すので注意。(R6RSの手続きでは、utf8は基本的にutf8表現のbytevectorを表す)
みづらいならpp(pretty-print)を使う。

(import (yuni lib ssax parsing) (http) (srfi :8) (mosh pp))
(receive (str _ __) (http-get->utf8 "http://search.twitter.com/search.atom?&q=scala")
   (pp (ssax:xml->sxml (open-string-input-port str) '())))

8. Find minimum (or maximum) in a List

(fold-left min 14 '(35 -7 46 98))
(fold-left max 14 '(35 -7 46 98))

これはScheme標準だけでできる。リストの先頭14を初期値とするために外に出している。

  • 追記

素直にSRFI-1のreduce使えという声も多いので:

(reduce min #f '(14 37 -7 46 98))

reduceは、(fold-left proc (car L) (cdr L))のように使える。ただ、空リストを渡したときに結果が不定にならないように"空リストを渡したときの結果"を明示的に与える必要がある。

9. Parallel Processing

いまのところmoshには便利な並列プリミティブが無い。将来的には付けたいがVMごと替えなきゃいけないので難しいところ。

10. Sieve of Eratosthenes

うーん。。この手のはスムースに書けない。Ruby版( http://programmingzen.com/2011/06/02/10-ruby-one-liners-to-impress-your-friends/ )のストレートな移植。

(import (srfi :1))
(let loop ((index 0)
           (primes (iota 100 2)))
  (if (<= (expt (list-ref primes index) 2)
          (car (reverse primes)))
    (loop (+ index 1)
          (let ((prime (list-ref primes index)))
            (filter (^x (or (= prime x)
                            (not (= 0 (mod x prime)))))
                    primes)))
    primes))

まとめ

表現力という点ではSchemeもそれなりの位置にいると思う。それなりの数の問題は1行だし。
が、いかんせんSRFI-hogeを覚えていないとコードが書けないのは不便な気がする。。
今のnmoshはREPLの編集機能が非常に貧弱なので、その辺は強化して行きたい。