Rust (4)

『皆殺しの天使』何かの小説のキャッチャーなコピーかと思ったら、違った。

『ココ・シャネル』(筑摩書房)なんて伝記を読んだ時に出てきたフレーズ。

19世紀的なゴテゴテ上流社会の服装を全て葬りさって、働きやすいシンプルな服を世に送り出した、 デザイナー兼実業家の、ガブリエル・シャネル女史の物語。

彼女は幼少の頃、母と死別。父は彼女を孤児院へ入れて姿をくらます。年齢制限で孤児院を 追い出された時、彼女はパリに出、帽子屋の見習いになる。夜は歌手を目指して、ナイトクラブの 歌手見習いに採用される。

そこで彼女が歌っていた、子犬のココの歌が、彼女の美貌と供に有名になり、あだ名で ココと呼ばれるようになる。

暗い孤児院時代に自分のアイデンティを出す為に、支給された服を自分なりに改造するすべを持っていた。 それが有名になり、パトロンの支持を得て、服飾デザイナーとしての道を歩み出す。

やがて、男性用の下着生地だったジャージー生地を大量に仕入れて困っていた生地業者から 破格の値段で生地を仕入れ、それで服を製作。これがヒットしてモード界で、その人の名を 知らぬ人はいなくなったと言われる。

彼女はほっそりとした体系で少女時代から、コンプレックスを持っていた。思い切って、髪を ショートにした所、思わぬ女らしさが生まれてにんまり。彼女いわく、

欠点は魅力の一つになるのに、みんな隠す事ばかり考える。欠点をうまく使いこなせばいい。 それさえうまくいけば、何だって可能になるのよ。 との事。

また、上流階級の人を皮肉って、

宝石は首の回りに小切手を付けているようなもの。もし宝石が何かの記号であるなら、それは 卑しさの、不正の、または老いの記号でしかない。 とも言っている。 強烈な個性ですなあ。

また、シャネルと言えば、香水も有名。服飾で成功した彼女は、香りの服飾にも乗り出そうと 考えていた。 その頃付き合っていたロシア貴族の紹介で、香水専門の化学者と知り合う。その頃は、ロシアが 香水の本場だったらしい。

彼女は、その化学者が調合した試験管のサンプルから、No.5を選び出す。5は、彼女の ラッキーナンバーだったとか。で、シンプルに香水の名前は、No.5。香水は余り流行り廃りも 無く、一度ヒットすると安定財源になるとか。

常日頃、山口県発祥の国民服で上から下まで身を固めているオイラーには、 コロンにもモードにも興味はないけど、面白く読めましたよ。たまには、こういう 畑違いな人の伝記もいいね。

国民服は、モードよりスタイル。ユニクロ・スタイルね。

計算機関連では、インテルモードよりも、armスタイル。それを集積した人が居て Raspberry Piスパコンで遊ぼうですって。 確かに、インテルの石は遊び憎いけど、ARMは庶民にフレンドリーですな。

getopts再び

前回、自前でgetoptsを書いたけど、気に入らない部分が有った。解析してるループ中で、 最後は数値でしょ、って事で強引に数値を取り出していた。数値でない場合は、まあ多分 エラーで落ちる。これを改善して、使い方を指南、プログラムを終了させたいな。

プログラムの終了って、exit()だな。osがらみだろうから、std::os に有るだろうと思った。 しかし、そんなの見つからず。ひょっとして、Windowsと互換性を考えたら、Windows側に exitなんて無いんで、諦めたか? 探す範囲を広めたら、libcで見つかった。

extern crate libc;
 
fn usage_exit(){
    :
    unsafe { libc::exit(0); }
}
 
fn getopts() -> Flag {
  :
            _     => av = match cv.parse() {  // Must be numbers
                Some(x) => x,
                None =>  { usage_exit(); 0 }, // 0 for adj type
            }

libcの中のものは、Rustに取っては要注意関数らしいので、その旨をunsafeで宣言して から使えとな。解析した時に値が無い場合は、使い方を指南して、その場で終了する ようには出来た。Rustの性格に合わせるために、変な調整をしたけどね。

その後、サンプルのgetoptsを覗いてみたら、

    let matches = match getopts::getopts(args.tail(), &opts) {
        Ok(m) => m,
        Err(f) => {
            println!("{}", f);
            os::set_exit_status(1);
            return;
        }

OkとかErrとかで包んであるのは、HaskellのEitherなんだな。あちらは、右左ってタグが付いてて、 Right(正しい)に対してLeft(不正)って読めよと、左効きを不当に貶めている。 その点、Rustは、何のひねりもなく、直球勝負で分かり易い。

まてよ、このRustのResult型は、読んだままに結果を表す型を想定してるけど、Eitherの方は、 右左で、どうとでも取れるようにボカシが入っているな。木として使っても違和感無いし、 結果型のように、正しい、不正の意味に使っても意味が通じる。旨いネーミングだ事。

で、注目は、set_exit_status()してからreturnしてる点。このコードが置かれているは、mainの中。 そこからreturnするって事は、プログラムの終了を意味する。

すなわち、Rustでは、関数の中でいきなりプログラムを終了させるような不作法な まねは、暗に望んでいないのだな。mainの中で、終了ステータスをセットしといてreturn させるのが、正しい終了方法とな。『人のふり見て、わが身を直せ』って、おばあちゃんの 戒めが身に沁みましたよ。

Uum このエラーは何?

Rustの文字列操作を参考に その後、えっちらおっちらと、Goから移植してたら、こんなエラーに出会ったぞ。

src/main.rs:47:33: 47:38 error: use of moved value: `a3[..]`
src/main.rs:47     let l = format!("{:8.1}", f(a3[1]));
                                               ^~~~~
note: in expansion of format_args!
<std macros>:2:26: 2:59 note: expansion site
<std macros>:1:1: 2:61 note: in expansion of format!
src/main.rs:47:13: 47:41 note: expansion site
src/main.rs:46:33: 46:38 note: `a3[..]` moved here because it has type `collections::vec::Vec<u32>`, which is non-copyable
src/main.rs:46     let h = format!("{:8.1}", f(a3[0]));
                                               ^~~~~

これ、書いていたコードがエラーになったので、単純化したものだ。

    45  fn ssf(f : fn(Vec<u32>) -> f64, a3 : [Vec<u32> ;3]) -> String{
    46      let h = format!("{:8.1}", f(a3[0]));
    47      let l = format!("{:8.1}", f(a3[1]));
                 :

ssf関数は、a3と言う配列と関数fを受け取って、配列のエレメント毎に、関数を適用して、 結果を文字列で返す部分。今回は自前のmaxを使ってる。47行目をコメントにすると、 何の問題もなく、コンパイルが通って、正しく実行される。

訳わかめだったので、 use of moved value error: なんて、聞いてみたぞ。そしたらヒントが返ってきた。借りてるぞ、借りてるぞマークと でも言うんでしょうか、、を付けないと許してもらえませんでした。権利関係全くもって 複雑。Rustを学びシステムレベル言語を理解すること

権利と言えば、最近KADOKAWAの『デジタル時代の知識創造』なんて本を読んだのだ。デジタルは 簡単にコピー出来るゆえ、著作権が大問題になっているとか。コピペ出来るのに、著作権が 有るから禁止ですってのは、犬に餌を見せておいて食べちゃ駄目って、不条理な事を 押し付けているに等しい。

それは無いよなと立ち上がったのは、古くはGNUの創始者RMSおじさん。新しくは一定な条件を満たせば コピペOKなクリエイティブ・コモン・ライセンス。略してCC。ローレンス・レッシグ教授が、 2002年に提唱。その条件とは

表示(BY) 作品のクレジットを表示する事
継承(SA) 元の作品と同じ組み合わせのCCライセンスを継承する事
改変禁止(ND) 元の作品を改変しない事
非営利(NC) 営利目的での利用をしない事

これらの選択子を組み合わせて一部の著作権を主張する。その権利主張に従う限りは 二次利用を許諾するというスタイル。よく、Webでも見かけるね。

最近TPPで著作権期間を50年から70年にしろと、日本に圧力をかけてくる国がある。 何でも、ミッキーマスス延命法の主張らしい。70年が過ぎたら、次は90年を主張するに 決まっている。せこい主張だ。

中国はイギリスから圧力をかけられて、昔、香港を分捕られそうになった。これを阻止 すべく交渉に当たった中国特使は、差し上げられませんが永久借用でいかがでしょうか? 中国では永久の事を久々と申します。これを数字に当てはめれば99。99年間の租借でいかがでしょうかって 振って、長引くと思われた交渉がわずか15分で終了させた。両者Win-Winで丸く収めた大人の対応じゃな。

そして、1997年にその約束が切れ、香港は中国に返還された。 発展させて貰っておいて、最後は利が得られると言う中国有利な結果。悠久な国だ事!

それに対し、ミッキーの国は、ただのごり押しだものなあ。駄々っ子だ事。

話が横道に逸れたな。Rust語で書いた、見るに耐えないコードを載せておきます。 CCライセンスは、BY、SA、NC ぐらいかな。って、クレジット書いてないじゃん。

つたないコード

#![allow(unstable)]

extern crate csv;
use std::path::Path;
use std::io::process::Command;
use std::os;
use std::io;                     // for read_line

static SEED: &'static str = "./2015.csv";

struct Flag {
    ap: i32,   // 0: OFF, 1: am, 2: pm
    tl: i32,   // 0: OFF, n: pick size
    pp: i32,   // 0: OFF, others: tags
    ss: i32,   // 0: OFF, 1: on
    lg: i32,   // 0: Off, 1: on
    er: i32,   // 0: Ok, others err
}

fn usage(){
    println!("{}", "Usage:");
    println!("{}", "\t-am\tPick wakeup data");
    println!("{}", "\t-pm\tPick night data");
    println!("{}", "\t-tl n\tPick last n data");
    println!("{}", "\t-pp\tDisplay selected data");
    println!("{}", "\t-ss\tDisplay stats");
    println!("{}", "\t-lg\tLine graph");
}

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

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 w(ds : &Vec<[u32 ;4]>) -> [Vec<u32> ;3] {
    let mut h = vec![];
    let mut l = vec![];
    let mut p = vec![];
    for el in ds.iter(){
        h.push(el[1]);
        l.push(el[2]);
        p.push(el[3]);
    }
    return [h,l,p];
}

fn max(a : &mut Vec<u32>) -> f64{
    let mut rv = 0u32;
    for v in a.iter(){
        if *v > rv { rv = *v; }
    }
    return rv as f64;
}

fn mean(a : &mut Vec<u32>) -> f64{
    let mut rv = 0u32;
    for v in a.iter(){ rv += *v; }
    return rv as f64 / a.len() as f64;
}

fn ssf(f : fn(&mut Vec<u32>) -> f64, a3 : &mut [Vec<u32> ;3]) -> String{
    let h = format!("{:8.1}", f(&mut a3[0]));
    let l = format!("{:8.1}", f(&mut a3[1]));
    let p = format!("{:8.1}", f(&mut a3[2]));
    return format!("{}{}{}", h, l, p);
}

trait Blood {
    fn pp(&self, i32) -> &Self;
    fn ampm(&self, i32) -> Vec<[u32 ;4]>;
    fn tl(&self, i32) -> Vec<[u32 ;4]>;
    fn ss(&self, i32) -> &Self;
    fn lg(&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 ampm(&self, cw : i32) -> Vec<[u32 ;4]> {
        let mut rv = vec![];
        match cw {
            1 => { for el in self.iter().filter(|v| v[0] % 100 < 12){
                       rv.push([el[0], el[1], el[2], el[3]]);  }}
            2 => { for el in self.iter().filter(|v| v[0] % 100 >= 12){
                       rv.push([el[0], el[1], el[2], el[3]]);  }}
            _ => { for el in self.iter() {
                       rv.push([el[0], el[1], el[2], el[3]]);  }}
        }      
        return rv;
    }

    fn tl(&self, cw : i32) -> Vec<[u32 ;4]> {
        let mut rv = vec![];
        match cw {
            0 => { for el in self.iter() {
                       rv.push([el[0], el[1], el[2], el[3]]);  }}
            _ => { let sz = self.len();
                   let n  = cw as usize;
                   for el in self.iter().skip(sz - n).take(n) {
                       rv.push([el[0], el[1], el[2], el[3]]);  }}
        }
        return rv;
    }

    fn ss(&self, cw : i32) ->  &Self {
        if cw != 0 {
            let mut a3 = w(self);
            println!("size: {}", self.len());
            println!("max: {}", ssf(max, &mut a3));
            println!("mean:{}", ssf(mean, &mut a3));
        }
        return self;
    }
    
    fn lg(&self, cw : i32) ->  &Self {
        if cw != 0 {
            let mut gd = String::new();
            gd.push_str("plot '-' w l\n");
            let mut cnt = 0i32;
            for el in self.iter(){
                gd.push_str(format!("{} {}\n",cnt, el[1]).as_slice());
                cnt += 1;
            }
            gd.push_str("\n");
            cnt = 0i32;
            for el in self.iter(){
                gd.push_str(format!("{} {}\n",cnt, el[2]).as_slice());
                cnt += 1;
            }
            gd.push_str("end\n");
//            println!("data: {}", gd);

            let mut process = match Command::new("gnuplot").arg("-p").spawn() {
                Err(why) => panic!("couldn't spawn gnuplot: {}", why.desc),
                Ok(process) => process,
            };
            let mut stdin = process.stdin.take().unwrap();
            match stdin.write_str(gd.as_slice()) {
                Err(why) => panic!("couldn't write gnuplot stdin: {}", why.desc),
                Ok(_) => println!("sent pangram to gnuplot"),
            }
            let dmy = io::stdin().read_line(); // pause for emacs run
        }
        return self;
    }
}

fn main() {
    let fg = getopts();
    if fg.er != 0 {
        usage();
        os::set_exit_status(fg.er as isize);
        return;
    }
    let bld = read_csv(SEED);
    bld . ampm(fg.ap) . tl(fg.tl) . pp(fg.pp) . ss(fg.ss) . lg(fg.lg);
}

実行してみる

[sakae@fedora bld]$ cargo run --verbose
       Fresh rustc-serialize v0.2.9
       Fresh csv v0.12.10
       Fresh bld v0.0.1 (file:///home/sakae/rust/bld)
     Running `target/bld`
Usage:
        -am     Pick wakeup data
        -pm     Pick night data
        -tl n   Pick last n data
        -pp     Display selected data
        -ss     Display stats
        -lg     Line graph
Process didn't exit successfully: `target/bld` (status=1)
[sakae@fedora bld]$ echo $?
1

この時点でrustcはお呼びじゃないので、バージョンは出てこない。エラーステータスは、 ちゃんとシェルに引き渡してくれてるね。

ちなみに、rustcとcargoのバージョンは、

[sakae@fedora bld]$ rustc -V
rustc 1.0.0-nightly (458a6a2f6 2015-01-25 21:20:37 +0000)
[sakae@fedora bld]$ cargo -V
cargo 0.0.1-pre-nightly (bb28e71 2015-01-22 06:06:34 +0000)

もう、1月も前のものか。たまには、新しくしてみるかな。

[sakae@fedora bld]$ target/bld -tl 0x10
Usage:
         :
[sakae@fedora bld]$ echo $?
2

ありゃ、parse関数じゃ、16進数は受けつけてくれないの? エラーになってるよ。

[sakae@fedora bld]$ target/bld -ss -tl 30 -am
size: 30
max:    138.0    79.0    63.0
mean:   125.7    73.7    56.2
[sakae@fedora bld]$ target/bld -ss -tl 30 -pm
size: 30
max:    136.0    79.0    67.0
mean:   118.3    67.9    62.3

これぐらい出来れば十分。勿論グラフも書けるしね。

で、ふと、血圧差と脈拍数の積は一定になるかと疑問が出てきた。ちょいと計算。

[sakae@fedora bld]$ gosh
gosh> (* (- 125.7 73.7) 56.2)
2922.4
gosh> (* (- 118.3 67.9) 62.3)
3139.919999999999

ふむ、夜の方が活動的って事かな。一日の刺激を脳コンピュータが一生懸命に処理 してるんで、血流が多いんだな。ちなみに、脳コンピュータの消費エネルギーは、20Wの 電球がほのかに灯るぐらいだそうです。 全血流の20%が脳に流れるとか。

ヒートアップ しないように、液冷ならぬ血冷になってるのね。液漏れは死に直結ですよ。注意しましょう。 って、どう注意すれば良いのやら!

おまけ

これも著作権に引っかかるかも知れんけど、元祖、2ch.netが、3月から 大幅な締め付けをするらしい。まとめサイトが幅を効かせていて、広告収入激減。 まとめサイトって、コピペだものね。

創始者のひろゆき氏は、古巣を追われて2ch.scを立てた。 古巣のDBのアクセス権を保持してんのかな。だとしたら、コピペじゃなくて、Backupだな。

こういうお家騒動に付込んで、open2ch.netが、大分前から 出来ている。

そして、Readitとかstackoverflowとか、三国志の世界ですなあ。オイラーは、吉川英治 さんの 三国志を、この歳になって読み初めていますよ。

更にコピペと言えば、コピペ天国の中国が、フィリッピンだかのコピペされたガンダム像に 文句を付けてますな。余りにもみすぼらしいと。コピペにも作法があるんだとかの主張。 さすが、コピペ覇者だわい。