Rust (3)

『遥かなるスキー』(実業之日本社)なんて本を読んだ。著者は杉山進さん。自伝である。

団塊の世代の人なら、ひょっとして杉山さんのスキー学校で、スキーを習ったかも知れないな。 あるいは、銀座ブラブラで、並木通りにある杉山進のスキーショップがあり、何で御茶ノ水 じゃないんだと、いぶかったかも知れない。

彼な1932年に、雪深い里、長野県野沢温泉に生を受ける。地元の小学校の体育の授業の一環として スキー運動会があり、そこで彼はおもいがけず2着になる。それまでは、余りスキーを好きでは 無かったと告白してる。

そうなんだよなー。雪が日常にあれば、わざわざ寒い中スキーに行くより、炬燵に潜って ミカンでも食べていた方が、よっぽど楽ですよ。オイラーもその気持ち、良く分かりますです。

2着が彼の人生の分かれ道になった。高校時代には、曲げ(アルペンスキーの回転の事)の競技で優勝。学生時代が終わっても、 スキーをやりたい。どうすれば? 休みの多い会社がいいな。変則勤務可能な、地元の私鉄に 入社。そこでスキー部に入り腕を磨く。

高じて、オーストリアに留学して、スキー教師の免許を取得。すんなりいく訳がなくて、 ドイツ語の学校にも通って頑張ったそうな。目的があるといいんすな。

これが縁になって、日本のスキー学校にオーストリアから教師が派遣されてくる。身長180cmを 優に超える長身。ある時、進先生と彼が、足を投げ出して壁によりかかり休んでいた。 何の気なしに横をみたら、真横に彼の顔が有る。そう、長身の彼もタンクな進先生も、座高は 一緒。

じゃ、何が身長差を生んでいる? 膝から下のすねの長さが決定的に違っていた。走る事を 生業にするヨーロッパ人と大地に根を張る日本人の骨格の違いなんですなあ。唖然としたとか。

進先生は、日本でスキーが流行するタイミングで学校を始められたゆえ、門下生が非常に 多く、かつ長く交流が続いているとか。 小沢征爾さん、白洲次郎さん、岡本太郎さん、猪谷千春さんはオイラーも知ってる。 他にはスペインの国王とか、どんだけ人脈が有るんだ?

人脈の場と言えば、ゴルフが筆頭だろう。でも、あちらは自然を改造して、人間の遊び場を 作っている。スキーだって一緒じゃん。林を切り開いて、ゴンドラをかけて、雪上車でピステを 作って。。。

いえ、オイラーが若かりし頃は、自然そのものがスキー場だったなあ。会社の先輩に連れられて 行った、八甲田山。あの時は、重い荷物で、死の行軍だった。でも、パラダイスだった。 登った分だけ滑られるって事だか、馬鹿は高い所に登だか知らないけど、ひたすら登って 滑り下りるを繰り返したなあ。

そして、夜は、青森駅前の市場で仕入れた、ホタテ貝を焼いたやつを肴に酒盛り。帰りは、 酸ヶ湯温泉で垢落とし。スキーと温泉がセットになってた。

乗鞍の雪渓も面白かったな。スプーンでほじくったように波打った斜面を滑り下りてくると、 まるで電気按摩器にかかった様。白骨温泉まで下って、温泉でゆったり。

いろいろ思い出して来るけど、この辺で。

ああ、最近バックカントリースキーとかで、遭難騒ぎが多発してるようだけど、 自然が相手ゆえ遊び方(遊ばれ方)のルールをわきまえてね。NHKのニュースで、 バックカントリースキーなんていきなり言うんで、何かと思ったら、山屋さんが楽しむ、 山スキーの事でした。洋風に言うと軽くなるなあ。冬山登山おまけにスキー付きだよ。 心してかかれ。

Golo

五郎もとえ、Goloってのが有るそうな。謳い文句は、 a lightweight dynamic language for the JVM. らしいです。

それってJVMな時点でlightじゃないだろう。重い言語って事なら、納得してやるんだけどな。 試してみたいけど、この間すっぱりとJavaに縁を切って、消しちゃったからなあ。今更Javaを 入れなおす季にならず。

で、これ、Rustにそっくりじゃん。ぱくりか? まあ、体系が似るのはしょうがないか。 同じ人間が考える事だからな。

フランスCITI Laboratoryとフランス国立応用科学院リヨン校(INSA-Lyon)による Dynamidグループの研究活動の成果として開発、2013年にオープンソースで公開した。

という事なんで、おふらんす生まれね。goにも似てるね。だから、go localで、両者を くっつけて、golo にしたんだな。まあ、頑張ってください。

try build

Rustをソースから味わおうってんで、原料をお取りよせして、チャレンジしてみた。 1時間ぐらいで、コンパイルが終わるよってのに騙されたぞ。3時間回し続けているけど まだ完成しない。

大体、工程の多すぎるよな。ステップ0,1,2,3って、どんだけーーー? 途中、種RustをこっそりDLしたのは知ってますよ。

goはソースから2分でコンパイル出来たけど、ググル様は、大変な事を考えているみたい。 Go 1.5のブートストラップ化を目指すGoogle 邪悪に激遅にならないようにね。

SEED

まずは、csvの例に出てたやつを発句として(コピペよりは上品でしょ)、種と言うか 叩き台を作ってみた。

最近、浅田次郎さんの『蒼穹の昴』なんて本を読んだんだけど、西太后のお付に、お仕置き のための棒叩き役人が居たんですって。気に入らない事があると、棒打ち30回なんて叫ぶと、 棒叩き役人が出てきて、叩いたらしい。恐いこった。

#![allow(unstable)]

extern crate csv;
use std::path::Path;
static SEED: &'static str = "./2015.csv";

fn read_csv(cf : &str) -> Vec<[u32 ;4]> {
    let fp = &Path::new(cf);
    let mut rdr = csv::Reader::from_file(fp).has_headers(false);

    let mut bld = vec![];
    let mut ary = [0xdeadbeefu32 ;4];
    for record in rdr.decode() {
        let (a,b,c,d): (usize, usize, usize, usize) = record.unwrap();
        ary[0] = a as u32;
        ary[1] = b as u32;
        ary[2] = c as u32;
        ary[3] = d as u32;
        bld.push(ary);
    }
    return bld;
}

fn pp(bld : Vec<[u32 ;4]>) {
    for el in bld.iter() {
        println!("{} {} {} {}", el[0], el[1], el[2], el[3]);
    }
}

fn main() {
    let bld = read_csv(SEED);
    pp(bld);
}

Rustでは、グローバル変数が禁止されている。オイラーの流儀として、プログラム冒頭で、 変更可能な値を宣言する事にしてる。(グローバル変数を使う事になるね)

今回はそれが出来ない? いいえ、プログラム実行中不変の値なら、宣言出来るよ。 いわゆる定数宣言ね。調べたら、冒頭のように、staticでいけるようだ。

また、変数は宣言と同時に初期値の設定を強制される。これは良い事ですね。でも、大きな 配列で、その中身もとなると大変。その為に便法が用意されてて、色々なブログにも 例が載ってる。

    found struct `core::ops::RangeTo`) [E0308]
main.rs:5     let myary = [123i, ..10];
                                 ^~~~

でも、オイラーの所ではエラったぞ。家庭教師は、最初のテンの所で、タイプミスマッチと 言ってくれてた。エラーは、大体検出したエラーの前にある。カンマが余分だった。

そりゃそうだわな。お前がもし文字列解析係りだったとしたら、カンマが出てきた所で、 ああ、一つのデータが確定したな。次のデータを解析しよう。何? 次はテンテン? そりゃないでしょってなる。

で、正しきは、スペース区切りで、データを確定した後、このデータをn回繰り返す 構文が来るとな。どうやらテンテンの代わりにセミコロンでも良いみたい。

また、数値の 初期値は、今は亡きSolarisを偲んで、deadbeefとかを唱えて挙げると、喜ばれます。 8桁の16進数だと、i32ではオーバーフローエラーを喰らうので、deadbee ぐらいが良いで しょう。

不恰好に並んでいる as は、C語で言う、castです。rustはasが好きなようで、 言い換えが欲しい時は、as ホニャラとすれば、コンテキストによっては認めてもらえる でしょう。

実験

ちょいとこれからの事を考えて、実験しとく。えと、余りの演算子はどうだっけ? それから、 平方根とかはどうするんだっけ?

#![feature(int_uint)]
use std::num::Float;

fn main() {
    let a = 23i;
    let b = 10i;
    let  c  = a % b;
    println!("{}", c);
    let x = 10_000f64.sqrt();
    let xx= (10_100 as f64).sqrt().sqrt();
    println!("{} {}",  x, xx);
}
3
100 10.024907

冒頭にある、featureとかは、referenceの項の6.3 Attributes に載ってた。gccとかでコンパイルする時に与える各種のフラグを、ソースの中に埋め込める ようにしたものだ。

sqrtなんてのは、余り出番が無いので常に使えるようにはなっていない。使う時は、数学 関係の浮動小数点モジュールをロードしろとな。

fn sqrt(self) -> Self
  Take the square root of a number.
  Returns NaN if self is a negative number.

この項が、Methodsの所に有った。メソッドってOO風な使い方が出来る奴。rubyとかで お馴染みの、メソッドチェーンが出来るかなと思ったら、出来たぞ。こりゃ、ベルトコンベアーと 言うか、Unixで言うPipeだな。使い道が有りそう。

数値は、10_000 みたいに、アンダーバーを入れて、見やすく出来る。正直、100万(円)みたいに、 余り庶民には縁が無い数値を設定する時は、桁ミスが少なくなって便利。日本だと、3桁区切り より、1000_0000 のように、4桁区切りの方が馴染みがあるか。いいえ、そんな高価な 物は、扱った事が有りませんから、心配無用です。(きっぱり)

OO風味でチェーン化

OOって、オブジェクト・オリエンテッドの事ね。rubyもpythonもこれ無しでは成り立たない。 rustでも出来るよと言うので、やってみた。

impl Blood for Vec<[u32 ;4]> {
    fn pp(&self) {
        for el in self.iter() {
            println!("{} {} {} {}", el[0], el[1], el[2], el[3]);
        }
    }
}

まあ、こんなものだろうと思って、コンパイルすると、

main.rs:26:6: 26:8 error: attempt to implement a nonexistent trait `Blood`
main.rs:26 impl Blood for Vec<[u32 ;4]> {
                ^~~~~

traitにBloodが無いよだってさ。traitって特性とか特色って意味なのね。OO風に言うと、 クラスなんだろうけど、名前を変えて、新規性を謳うのはマーケッティングの基本です。

最近では、ココナッツ・オイルが流行ですなあ。椰子油の事でっしゃろ。 どうせ、どこかの香具師と某メーカーがタッグを組んでの事でしょう。女房は買占めに 走っていたぞ。通販では売り切れが続出ですってさ。

trait Blood { fn pp(&self); }

しょうがないので、こういうのを用意しましたよ。C語で言う型宣言ね。OO風なやつだと、 Bloodってクラスには、ppって言うメソッドが有るよ。実際の中身は、impl宣言の中に書く事に なってる。Bloodってクラスはは for T で示される型のもんだからね。めんどう臭いな。

面倒だけど我慢して、上記をちょっと改造。チェーン出来るようにする。折角だから、 ppはプローブの役目をさせるで、タイトル(の数字)を表示出来るようにしてみる。

trait Blood {
    fn pp(&self, i32) -> &Self; 
}

impl Blood for Vec<[u32 ;4]> {
    fn pp(&self, cw : i32) -> &Self {
        if cw != 0 {
            println!("--> {}", cw);
            for el in self.iter() {
                println!("{} {} {} {}", el[0], el[1], el[2], el[3]);
            }
        }
        return self;
    }
}

fn main() {
    let bld = read_csv(SEED);
    bld.pp(1).pp(2);
}

プローブなんで、受け取った引数をそのまま返す所が、味噌です。 cwにZEROを渡すと、表示せずにスルーするようにしといた。debugツールの一種だから、 簡単にON/OFF出来たらいいなと思っての、思いやり仕様です。

--> 1
15010104 130 73 54
  :
--> 2
15010104 130 73 54
  :

これで、取り合えず、入出力部分は出来た。次はeval部門だな。この場合は各種フィルターが それに相当するけど。。。

今回は、そのフィルターを、パイプ相当のメソッド・チェーンにザーと並べて置く積もり なんだけど、個々のフィルターのON/OFF制御の事も考えておかんとな。

そんなの、ppで実装済みじゃん。cwにZEROを渡すと、スルーするって事にしとけばいい。 そしてZERO以外は、有意なな値って事にしよう。今考えたAPIです。これで矛盾は無いよね。

getopts 相当品

パイプ中に埋め込まれたフィルターのOn/Off制御は、コマンドラインから指定する事に する。もう過去にgoの例題でもやった。

goの場合は、コマンドラインをパースしてくれるFlagなんてパッケージが有ったけど、Rustの 場合は如何に? そのものずばりのgetopts ってのが有った。本来ならこれを使うのが筋だろうけど、車輪の再発明をしてみる。

小手調べに基本を

use std::os;

fn main() {
    let flag = os::args().to_vec();
    for f in flag.iter(){  println!("{}", f); }
}
[sakae@fedora testme]$ cargo run 123 456
     Running `target/testme 123 456`
target/testme
123
456
[sakae@fedora testme]$ cargo run 789 -abc
Usage:
    cargo run [options] [--] [<args>...]
[sakae@fedora testme]$ target/testme 789 -abc
target/testme
789
-abc

小手調べと軽く考えていたら、いきなり、cargoから面を喰らいましたよ。 こういうのを面食らうと言いますね。cargoから起動すると、 cargoの間合いの中なのね。防御法がさりげなく提示されたぞ。。

[sakae@fedora testme]$ cargo run -- 789 -abc
     Running `target/testme 789 -abc`
target/testme
789
-abc

バーバーで、バリアーを貼るといいんですね。

頭の中で、構想を練る。オプションを処理する関数名は、先輩に習ってgetopts。解析結果は、 構造体に入れて返す事にする。

use std::os;

struct Flag {
    ap: i32,   // 0: OFF, 1: am, 2: pm
    tl: i32,   // 0: OFF, n: pick size
}

fn usage(){
    println!("{}", "\t-am\tPick wakeup data");
    println!("{}", "\t-pm\tPick night data");
    println!("{}", "\t-tl n\tPick last n data");
}

fn getopts() -> Flag {
    let mut op = Flag { ap: 0i32, tl: 0i32 };
    let mut flag = os::args().to_vec();
    if flag.len() == 1 { usage(); return op;}
    flag[0] = "fin".to_string();
    let mut av = 0i32;
    loop {
        let top = match flag.pop() { None => break, Some(x) => x, };
        let cv = top.as_slice();
        match cv {
            "fin" => break,
            "-am" => op.ap = 1,
            "-pm" => op.ap = 2,
            "-tl" => op.tl = av,
            _     => av = cv.parse().unwrap(),
        }
   }
   return op;
}

fn main(){
    let fg = getopts();
    println!("{}", fg.tl);
}

引数が無い場合は、使い方を表示して復帰する。なお、引数が無くても、実行プログラム名が 載ってくるので、配列のサイズは1となる。

配列の一番頭には、実行プログラム名が入るんだけど、いわゆるPath名で、実行方法に よって変化する。解析が面倒になるんで、強引に、fin なんて言う名前に置き換えて いる。後ろに付いている、to_string() とか言うのは、Rust特有の、文字列の2面性を 吸収する方策だ。詳しい事は、 Rustの文字列のガイド あたりを参照。

解析の都合上、コマンドラインの最後から、前に向かって行う。一番前は、finに置き換えて おいたので、それが出てきたら、解析終了って仕掛け。

何故後ろから前へと評価するか、それはオイラーがforthファンだから!! オプションに よっては、-tl n みたいに、数値を取るのがある。前から解析すると、先読みとか、とかく 面倒そう。

後ろからなら、数値が出てきたら、それを退避。-tlが出たら、退避したのを割り当てって 具合でいいから。じゃ、数値が必要なのに、指定が無い、-tlが出てきたら? そりゃ、 エラーにしなければいけないけど、簡易型なんで、そこは見なかった事にします。

配列の最後を取り出すのにpop()を使っている。こやつは、空配列に適用すると、エラーに なる。それを避ける為、失敗するかも知れない演算って事で、丁度Haskellのmaybeみたいに、 失敗を回避する機構が組み込まれている。失敗した時はNone、成功した時はSome(x)が 返ってくるので、そのSomeを引っ剥がす事をやってる。(std::vec からの受け売りなんですけどね)

次はpopで取ってきた(文字列)ものをマッチさせればいいんだけど、ここでもRustの文字列特性が 出てきてるんで、as_slice()で補正をかけたもので、比較してる。比較の最後には、必ず、 何でもマッチする _ を置く事になってる。

この辺は、Lispのcond節の最後に置く、t の事と思えば良い。ああ、Rustのmatchは、condに 比べて、不自由ではあるんですが。。。 Rustの売りって事になってて、そんなの、50年前に 既にやられているよ、、、なんて事を言ってはいけません。

何でもマッチは、数字だと思って、parseしてる。勿論失敗するかも知れない演算なんだけど、 そこは手抜きで、失敗無しとして、Someを引っ剥がずunwrap()でお茶を濁している。まあ、 maybeは、程よく使ってって事で。。。

後は、この手抜きgetoptsを本格版と比べて、どういう風にやってるか技を盗めばいいんだな。 昔から良く言われる、『人のふり見て、わが身を直せ』。これ、おばあちゃんの口癖だった からなあ。