Nendo.Tutorial RSSPLAIN

Related pages: Nendo Nendo.ReferenceManual kiyoka.2010_05_01 kiyoka.2010_04_13 kiyoka.2010_03_19 !kiyoka.blog.2010_09 !kiyoka.blog.2010_07 !kiyoka.blog.2010_05 !kiyoka.blog.2010_04 !kiyoka.blog.2010_03
5510155555552555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555554544555544445555555555555555555555555555555555555522222222222222222222222255555553535555555355555555335333333334355555555555555555555555555555555555555555555555555555555
5

Nendo programming language Tutorial

5

This is a tutorial document of Nendo programming language. This is available only in Japanese.

1

 

0
 iStock_000007399909XSmall
1

 

5

はじめに

5

NendoはS式でRubyプログラミングを行うためにデザインされた言語です。

5

このチュートリアルはある程度のRuby、Lisp系言語の両方のプログラミング経験があれば楽に読むことができるでしょう。

5

また、Nendoの文法はできるかぎりSchemeに似せてデザインされているため、Schemeでのプログラミング経験があれば、より助けになるでしょう。

5

もし、RubyにもLispにもあまり馴染みが無い方でも、本チュートリアルを順番に読んでいけば、どのような言語なのかという雰囲気はつかめるはずです。

5

 

5

対応バージョン

2

本マニュアルは Nendo version 0.6.5に対応しています。

5

 

5

replの起動 ( interactive shell )

5

コマンドラインオプション無しで起動すると nendo> というプロンプトが表示され、入力されたS式がNendoのトップレベルの式として評価されます。

5
$ nendo
5
nendo> (+ 1 1)
5
2
5
nendo> (+ "1" "1")
5
"11"
5
nendo> (iota 10)
5
(0 1 2 3 4 5 6 7 8 9)
5

 

5

 

5

ユーザースクリプトの実行

5

スクリプトを指定して実行する方法

5

nendoコマンドにスクリプトファイルを指定して実行します。

5
nendo your_script.nnd
5

 

5

ユーザースクリプトをコマンド化する方法(1)

5

ある程度満足できるプログラムができたら、ユーザースクリプトファイルを単独で起動したくなるでしょう。

5

その場合は、ファイルの先頭に下記のおまじない(3行のシェルトランポリン)を記述して、スクリプトファイルに実行権限を設定してください。

5
#!/bin/sh
5
:; #-*- mode: nendo; syntax: scheme -*-;;
5
:; exec /usr/local/bin/nendo $0 $*
5
5
 ... your code ...
5
5

※ 注意: NendoはスクリプトファイルのエンコードをUTF-8として読み込みます。UTF-8以外のエンコードを使ってプログラミングすることはできません。

5

 

5

ユーザースクリプトをコマンド化する方法(2)

5

コマンド化する方法はもう一つあります。

5

こちらの方法でコマンド化すれば、gem buildでgem化しても正しくコマンドラインから実行できるようになります。

5

注意点は、main関数の呼出しが行われなくなることです。

5

スクリプトの最後でmain関数を自前で呼ぶなど対処してください。

5
#!/usr/bin/env ruby
5
# -*- mode: nendo; syntax: scheme ; coding: utf-8 -*-
5
require 'nendo'
5
$LOAD_PATH.push( File.dirname(__FILE__) + "/../lib" )
5
core = Nendo::Core.new()
5
core.setArgv( ARGV )
5
core.loadInitFile
5
core.evalStr( <<";;END-OF-SCRIPT" )
5
5
 ... your code ...
5
5
;;END-OF-SCRIPT
5

 

5

スクリプトの最後でmain関数を呼ぶ場合、ファイルの最後を以下のように書き換えて下さい。

5
(main *argv*)
5
;;END-OF-SCRIPT
5

 

5

 

5

簡単な式を評価してみよう

5

Schemeに似たsyntax

5

NendoのsyntaxはSchemeライクです。Schemeに慣れているなら直接その知識を応用することができます。

5

まずは、ハローワールドです。

5
(print "Hello, World!")
5
   => Hello, World!
5

 

5
(display "Hello, ")
5
(display "World!")
5
(newline)
5
   => Hello World!
5

 

5

文字列を連結するバージョンです。

5

Schemeと違って文字列は + 関数で結合できます。

5
(print (+ "Hello, " "World!"))
5
   => Hello, World!
5

 

5

関数定義してみましょう。

5
nendo> (define (hello-world str)
5
         (print (+ "Hello, " str " World!")))
5
   => #<Proc:0x7cc688@(stdin):23>
5
nendo> (hello-world "Nendo")
5
Hello, Nendo World!
5
   => "Hello, Nendo World!"
5

 

5

文字列のフォーマッティングはRubyではおなじみのprintf系が使えます。

5
nendo> (define str "Nendo")
5
   => "Nendo"
5
nendo> (sprintf "Hello, %s World!" str)
5
   => "Hello, Nendo World!"
5

 

5

 

5

Rubyとの連携

5

NendoはsyntaxこそSchemeですが、使用できる文字列や数値のオブジェクトは全てRubyのオブジェクトそのものです。

5

.(ドット) 記号を使って、Rubyのインスタンス変数やメソッドにアクセスすることができます。

5
nendo> (define str "abc")
5
"abc"
5
nendo> (str.class)
5
String
5
nendo> (str.size)
5
3
5
nendo> (. str size)
5
3
5
nendo> (. "1234" size)
5
4
5

 

5

インスタンスを省略するとKenrel::を指定したことになります。

5
nendo> (define f (.open "file.txt"))
5
#<File:0x00000101463320>
5
nendo> (f.readline.chomp)
5
"first line."
5

 

5

次は、新しいRubyのインスタンスを生成して使う例です。(Dateクラスを生成する例)

5
(require "date")
5
(let1 d (Date.new 0)
5
  (list (d.strftime "%x")
5
        (d.strftime "%s")))
5
   => (01/01/00 -62167392000)
5

(require "date")はRubyのライブラリを使うための宣言でRubyのrequireと等価です。

5

 

5

Digest::MD5クラスをインスタンス生成せずにクラスメソッドを直接呼び出す例です。

5
nendo> (require "digest/md5")
5
   => false
5
nendo> (define digest (Digest::MD5.hexdigest "buzz buzz buzz"))
5
   => "75ced500ba12b8e25475b9fce170a914"
5
nendo> digest
5
   => "75ced500ba12b8e25475b9fce170a914"
5

 

5

スクリプトを作ってみよう

5

 

5

階乗を求めるプログラムに挑戦

5
fact.nndという名前でファイルを開きます。
5
以下のコードを入力して、保存します。
5
#!/bin/sh
5
:; #-*- mode: nendo; syntax: scheme -*-;;
5
:; exec /usr/local/bin/nendo $0 $*
5
5
;; fact
5
(define (fact n)
5
  (if (zero? n)
5
      1
5
      (* n (fact (- n 1)))))
5
5
(print (fact 5))
5

 

5
fact.nndに実行可能フラグを付けてください。
5
chmod +x ./fact.nnd
5

 

5
実行します。
5
$ ./fact.nnd 
5
120
5

1 から 5 までの階乗を求めて表示するプログラムができました。

5

 

5
1 から 10 までの階乗を求めます。
5

fact.nndの最後の行の fact関数に与える引数を 10 に変更します。

5
(print (fact 10))
5
実行します。
5
$ ./fact.nnd 
5
3628800
5

 

5

もうひとつの階乗プログラム

5

上記と同様の計算は関数型プログラミングのスタイルを使ってもうすこし簡潔に書くことができます。

5

 

5

5の階乗を求めたい場合は まず 1 から 5 数字を作ります。

5
$ nendo
5
nendo> (range 5 1)
5
(1 2 3 4 5)
5
 ※ (rangeはiotaの別名です)
5

 

5

ここまでくれば簡単です。

5

全ての数字をかけ算すれば良いので、applyで * 関数に引数として渡してしまいましょう。

5
nendo> (apply * (range 5 1))
5
120
5

求まりました。

5

※ このapplyの効果は (* 1 2 3 4 5) という式を書いたのと同じです。

5

 

5

10の階乗も同じです。

5
nendo> (apply * (range 10 1))
5
3628800
5

 

5

このように、ループや再帰を使わずに、リスト生成と関数適用という形で解決することができます。(関数型プログラミングスタイル)

5

 

5

コマンドライン引数でパラメータを渡す

5

main関数を定義すると、引数にコマンドライン引数を受けとることができます。

5

Nendoはスクリプト中にmain関数が存在すれば、起動直後に呼び出します。

5

引数argvの型は文字列のリストです。

5

[argv.nnd]

5
#!/bin/sh
5
:; #-*- mode: nendo; syntax: scheme -*-;;
5
:; exec /usr/local/bin/nendo $0 $*
5
(define (main argv)
5
  (write argv)
5
  (newline))
5

 

5

コマンドラインで実行してみます。

5
$ nendo argv.nnd a bb ccc
5
("a" "bb" "ccc")
5

 

5

先程出てきたfact.nndをNの階乗を求めるプログラムにしてみましょう。

5

[fact.nnd]

5
#!/bin/sh
5
:; #-*- mode: nendo; syntax: scheme -*-;;
5
:; exec /usr/local/bin/nendo $0 $*
5
5
;; fact
5
(define (fact n)
5
  (apply * (range n 1)))
5
5
(define (main argv)
5
  (print (fact (. (car argv) to_i))))
5

 

5

実行してみます。

5
$ nendo fact.nnd 1
5
1
5
$ nendo fact.nnd 2
5
2
5
$ nendo fact.nnd 3
5
6
5
$ nendo fact.nnd 4
5
24
5
$ nendo fact.nnd 5
5
120
5
$ nendo fact.nnd 10
5
3628800
5
$ nendo fact.nnd 20
5
2432902008176640000
5

 

5

 

5

Rubyのデータ構造を作る

5

 

4

Array ( vector )

5

RubyのArrayを作るためにはいくつかの方法があります。

4

Nendoのvectorという型はRubyのArrayと等価です。※ vectorはR5RS Schemeで定義されているvectorでもあります。

4

 

5

listからArrayに変換する

5
nendo> (to-arr '(1 2 3))
5
#(1 2 3)
5

 

4

Arrayからlistに変換する

4
nendo> (to-list '#(1 2 3))
4
(1 2 3)
4

 

5

vectorを作る

5
nendo> (define vec '#("a" "b" "C" "d" "e"))
5
#("a" "b" "C" "d" "e")
5
nendo> (vec.class)
5
Array
5
nendo> (vector-ref vec 2)
5
"C"
5

 

5

Hash

5

RubyのHashを作るためにはいくつかの方法があります。

5

alistからHashに変換する

5

alist->hash-table関数を使ってalistをHashに変換します。

5
nendo> (define assoc-list '( ("a" .  1) ("b" .  2) ("c" .  3) ))
5
(("a" . 1) ("b" . 2) ("c" . 3))
5
nendo> (define h1 (alist->hash-table assoc-list))
5
{"a"=>1, "b"=>2, "c"=>3}
5
nendo> (h1.class)
5
Hash
5
nendo> (hash-table-get h1 "b")
5
2
5
nendo> (hash-table-keys h1)
5
("a" "b" "c")
5

hash-table->alist関数を使って、逆変換も可能です。

5
nendo> (hash-table->alist h1)
5
(("a" . 1) ("b" . 2) ("c" . 3))
5

 

5

hash-table系関数群を使う

5
nendo> (define h2 (make-hash-table))
5
{}
5
nendo> (hash-table-put! h2 "aa" 11)
5
11
5
nendo> (hash-table-put! h2 "bb" 22)
5
22
5
nendo> (hash-table-keys h2)
5
("aa" "bb")
5
nendo> (h2.class)
5
Hash
5

 

2

 

2

ファイルからの入力

2

ファイルからデータを1行づつ入力する処理はスクリプトでは頻繁に使います。

2

Nendoでは次のようなイディオムになります。

2
 [Nendo]
2
(with-open "textfile"
2
  (lambda (f)
2
    (for-each 
2
      (lambda (line)
2
        (print (+ "# " line)))
2
    (readlines f))))
2

※ Ruby 1.9.xでは、readlinesは一度にファイルの最後まで読み込みvectorを返しますが、

2

Ruby 2.0では readlines 全て読み込まず、Enumerator::Lazy 型の遅延vectorを返します。

2

 

2

 

2

典型的なフィルタ

2

NendoはRubyのSTDINがそのまま使えます。

2
(for-each
2
  (lambda (line)
2
    ;; 出力処理
2
    (print line))
2
   STDIN)
2

 

2

 

5

Rubyのブロック構文を作る

5

&block構文を使えば、Rubyのブロックそのものが生成可能です。

5

&block構文を用意した理由は、多くのRuby gemライブラリがブロックを引数に取るメソッドを持っているためです。

5

 

5

Rubyコードとの比較で見る

5

例えば、入力テキストsample.rb の全行をコメントアウトするプログラムは以下のように書くことができます。

5
 [Ruby]
3
open ( "sample.rb" ) {|f|
5
  f.readlines.each {|line|
3
    puts "# " + line
5
  }
5
}
5
 [Nendo]
5
(.open "sampel.rb"
5
  (&block (f)
5
    (f.readlines.each
5
      (&block (line)
3
         (print (+ "# " line))))))
5

 

5

※ 上記は、&block構文を説明するために、故意に&blockを使用したスタイルで記述しています。

5

通常は、リストとlambdaを使った下記のようなScheme的なスタイルのほうが自然でしょう。

5
 [Nendo]
5
(with-open "sample.rb"
5
           (lambda (f)
5
             (for-each
5
              (lambda (line)
3
                (print (+ "# " line)))
3
              (to-list (f.readlines.to_list)))))
5

 

3

また、Nendoのfor-each/map/filter関数はリストの変わりにEnumerable型を取ることができるので次のように記述できます。

3
 [Nendo]
3
(with-open "sample.rb"
3
           (lambda (f)
3
             (for-each
3
              (lambda (line)
3
                (print (+ "# " (line.chomp))))
3
              f)))
4

 

3

 

5

デバッグ

5

Nendoのプログラムをデバッグする手段としては、printデバッグが基本です。

5

手段として専用のリーダーマクロ #?= が用意されています。

5

 

5

マクロ #?=

5

S式に #?= を付けると、その式のソースファイル名、行番号、評価結果が表示されます。

5

また、#?= は副作用がありませんので、何処にどれだけ挿入していっても元のコードに影響を与えません。(Rubyのp関数に似ていますね)

5

例1:

5
(define a 100)
5
100
5
nendo> (+ #?=a 1)
5
#?="(stdin)":8:a
5
#?-    100
5
101
5

 

5

例2:

5
 [debug-sample.nnd]
5
(print
5
 (map
5
  (lambda (x)
5
    #?=(*
5
        #?=x
5
        2))
5
  #?=(range 5)))
5

 

5
 [実行結果]
5
 $ nendo ./debug-sample.nnd 
5
#?="./debug-sample.nnd":7:(range 5)
5
#?-    (0 1 2 3 4)
5
#?="./debug-sample.nnd":4:(* #?=x 2)
5
#?="./debug-sample.nnd":5:x
5
#?-    0
5
#?-    0
5
#?="./debug-sample.nnd":4:(* #?=x 2)
5
#?="./debug-sample.nnd":5:x
5
#?-    1
5
#?-    2
5
#?="./debug-sample.nnd":4:(* #?=x 2)
5
#?="./debug-sample.nnd":5:x
5
#?-    2
5
#?-    4
5
#?="./debug-sample.nnd":4:(* #?=x 2)
5
#?="./debug-sample.nnd":5:x
5
#?-    3
5
#?-    6
5
#?="./debug-sample.nnd":4:(* #?=x 2)
5
#?="./debug-sample.nnd":5:x
5
#?-    4
5
#?-    8
5
(0 2 4 6 8)
5
 $
5

 

5

 

5

TODO  このチュートリアルは書きかけです。まだまだ続く予定です。

5

 

5

...comment disabled...