main手続き

今まで、Scheme のスクリプトは頭から順に実行されるもんだと思っていたけど、そうでもないらしい。main という名前の関数(Scheme では手続きというのか)が定義されていると、それがスクリプトのエントリポイントになる。

 cf. 3.3 Schemeスクリプトを書く – Gauche ユーザリファレンス

ファイルが正常にロードされたら、goshは userモジュールに ‘main’ という手続きが定義されているかどうか調べ、 定義されていればそれを呼びます。mainには、スクリプトへの引数のリストが 唯一の引数として渡されます。リストの最初の要素はスクリプトファイル名です。

試してみよう。

(define main
  (lambda (args)
    (print args)))

実行:

^o^ > gosh main.scm foo bar baz
(main.scm foo bar baz)

確かに main 手続きが実行された。

じゃあ、昨日の nend.scm を main 手続きを使って、引数をコマンドラインから渡せるように変更してみよう。

(define divmod
  (lambda (n m)
    (list (div n m) (modulo n m))))

(define mul
  (lambda (n)
    (let loop ((dm (divmod n 10)) (l '()))
      (if (= (car dm) 0)
          (apply * (cons (cadr dm) l))
          (loop (divmod (car dm) 10) (cons (cadr dm) l))))))

(define nend
  (lambda (n)
    (let loop ((x n) (i 0))
      (if (< x 10)
          i
          (loop (mul x) (+ i 1))))))

(define nend_smallest
  (lambda (n)
    (let loop ((i 1))
      (if (= (nend i) n)
          i
          (loop (+ i 1))))))

(define main
  (lambda (args)
    (print (nend_smallest (string->number (cadr args))))))
^o^ > gosh nend2.scm 2
25

^o^ > gosh nend2.scm 3
39

^o^ > gosh nend2.scm 4
77

^o^ > gosh nend2.scm 5
679

うまくいった。

ところで、今まで書いてきたみたいに手続きの外に (print ...) があるとどうなるんだろう。

(print "Hello, world.")

(define main
  (lambda (args)
    (print args)))

実行してみると:

^o^ > gosh main2.scm foo bar baz
Hello, world.
(main2.scm foo bar baz)

(print "Hello, world.") と main 手続きが両方実行されたよ。