Rustのsplit_atで日本語を使う時の注意点

O'ReillyのProgramming Rust Chapter 3: Basic TypesTuplesに出てくるsplit_atという関数について、ちょっとつまづいたところがあったのでメモ。

本題

Rustにはsplit_atという関数があり、

let value = 2;
let text = "abcde";
let (head, tail) = &text.split_at(value);
println!("{}", head);                       // => ab
println!("{}", tail);                       // => cde

という、文字列上の指定した位置で切り分け、それをタプルにして返すというものである。しかし、

let value = 2;
let text = "あいうえお";
let (head, tail) = &text.split_at(value);
println!("{}", head);
println!("{}", tail);

を実行すると、

thread 'main' panicked at 'byte index 2 is not a char boundary; it is inside 'あ' (bytes 0..3) of `あいうえお`', libcore/str/mod.rs:2108:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

と怒られてしまう。これは、'あ'はUTF8だと3バイトであり、2charの境界ではないと言っている。

結論

日本語をsplit_atするときは3の倍数の数値を使う必要があるので、

let value = 2;                              // 切り取りたい位置
let text = "あいうえお";
let (head, tail) = &text.split_at(3*value);
println!("{}", head);                       // => あい
println!("{}", tail);                       // => うえお

といった感じにする。ただし、日本語と英語が混ざったりしているときは有効でないので注意。

Rustの{integer}および{float}

O'ReillyのProgramming Rustを読んでいて、不思議に思った部分があったのでメモ。

前提となる知識

  • Rustでは最低限の型の情報さえ与えれば、残りの部分は推論される。たとえば、

    fn sample1() -> i32 { let x:i32 = 100; x }

    という関数において、戻り値の型から、戻り値xi32となるのは自明なので、

    fn sample2() -> i32 { let x = 100; x }

    と書ける。

  • Rustでは、Cでintlongに相当する型はそれぞれi32i64floatdoubleに相当する型はそれぞれf32f64になっている。

  • i32i64のどちらにも推論できる場合はi32となる。それ以外の場合は、曖昧さをエラーとして報告する。

  • f32f64のどちらにも推論できる場合はf64となる。それ以外の場合は、曖昧さをエラーとして報告する。

本題

Chapter 3. Basic TypesFloating-Point Typesには

println!("{}", (2.0).sqrt());

を実行すると、

error: no method named `sqrt` found for type `{float}` in the current scope

といったエラーが出るが、

println!("{}", (2.0_f64).sqrt());
println!("{}", f64::sqrt(2.0));

などとすれば、きちんと実行されると書かれている。

(2.0)浮動小数点数であることは自明であり、Rustはf64に優先して推論されるはずだが、なぜだかうまく推論してくれない。変数に代入するときなどに推論され、(2.0).sqrt()を実行した時点ではうまく推論してくれないのかと思い、

let x = 2.0;
println!("{}", x.sqrt());

としてみたが、これも先ほどと同様のエラーが出てしまう。

日本語のRustのドキュメントには

let y = 1.0; // yはf64型を持つ

と書かれているので、上記のような書き方でも問題はないはずである。

結論

ここここをみる感じでは、どうやらこの現象はrustcのバグっぽい…?

また、{float}だけでなく{integer}でも起こる場合がある模様。

参考文献

http://ykomatsu.github.io/rust/book-ja/README.html

Rustの関数の引数でmutを使ったときの挙動

Rustの変数はデフォルトでイミュータブルである。たとえば、

let x = 10;
x = 20;

はエラーとなる。値を更新したい場合は、

let mut x = 10;
x = 20;

とする必要がある (mutmutableの略) 。

本題

fn sample(mut n: i32) -> i32 {
    n += 1;
    n
}

fn main() {
    let n = 10;
    println!("n_before  = {}", n);
    println!("sample(n) = {}", sample(n));
    println!("n_after   = {}", n);
}

を実行すると

n_before  = 10
sample(n) = 11
n_after   = 10

が出力される。mutableといっても、Javaとは違って参照値を渡しているわけではなく、純粋に値を後から代入しなおせるようにするだけの模様。

Pythonの変数名

Pythonでちょっとつまづいたのでメモ。

Javaとかだと

class sample{

    static int f(int x){
        return x*2;
    }

    public static void main(String[] args) {
        int f = 3;
        System.out.println(f(5));
    }

}

10と出力される。メソッド名と変数名がかぶっても良い。

しかし、Pythonだと、

def f(x):
    return x*2

f = 3
print(f(5))

というプログラムを実行すると、

Traceback (most recent call last):
  File "sample.py", line 5, in <module>
    print(f(5))
TypeError: 'int' object is not callable

となってしまう。よく考えてみると動的型づけを行っているので、当然であった。

Ruby 1.9.3のインストールについて

諸事情からRubyの1.9.3をrbenvを使いインストールしようとしたが、私の環境のArch Linux (Antergos) では少々つまづくポイントがあったのでメモ。

エラーメッセージは以下のようになっていて、

BUILD FAILED (Antergos Linux using ruby-build 20180822)

Inspect or clean up the working tree at /tmp/ruby-build.20180911160152.13741
Results logged to /tmp/ruby-build.20180911160152.13741.log

次のようにすることで解決した。

$ curl -fsSL https://gist.github.com/FiveYellowMice/c50490693d47577cfe7e6ac9fc3bf6cf.txt | PKG_CONFIG_PATH=/usr/lib/openssl-1.0/pkgconfig:/usr/lib/pkgconfig rbenv install --patch 1.9.3-p551

参考文献

HAL9000っぽいスマートスピーカーを作る

はじめまして。突然ですが、SF映画の名作「2001年宇宙の旅」に登場するAIである「HAL9000」を精密に再現したスマートスピーカーが2018年8月現在クラウドファンディングで出資を募っています。しかし、出資を募っている段階ではいつ出荷されるのかはわかりません。その上出資額が最低でも419ドルと少々お高いです。なので「もういっそのこと自分で作れば良いのでは」という結論に至り、作ることにしました。

仕様

私が作ったものは以下のような仕様となっています。

  • Google Assistantの一つのアプリとして開発。Google Assistantに「OK Goolge, HAL9000とつないで」と喋りかけることで起動させる。
  • 起動中に映画内のセリフ(定型文)で話しかけると、HAL9000を再現したアプリが映画内のセリフを(音声ファイルを再生して)返す。
  • Raspberry Pi上でGoogle Assistantを動かし、スピーカーおよびマイクとRaspberry PiをつないでHAL9000っぽくする。
  • クラウドファンディングで出資を募っているスマートスピーカーのようなカッコいい見た目のものではない。ダンボールに紙を貼る小学生の自由研究並みの見た目になる予定。

また、この記事では、基本的に行ったことをたんたんと述べていくだけなので、詳しい解説は参考記事を参照してください。

今回はGoogle Assistant部分のみを構築します。具体的には、Google Assistantに「OK Goolge, HAL9000とつないで」と喋りかけるとHAL9000を再現したアプリを起動させ、映画内のセリフで話しかけると映画内のセリフを返すようにします。次回でRasberry Piを使ってガワを作ります。

プロジェクトの作成

まず、Actions on Googleに接続し、「Add/import project」で新規プロジェクトを作成します。

f:id:namakura878:20180819181602p:plain

Project Nameは英字で決め(ここではHAL9000とした)、"Choose the default language for your Actions"でJapanese、"Choose your country or region"でJapanを選びます。言語設定は後からでも追加できます。

"Welcome to your project, HAL9000!"というページが出てくるので、「skip」で次に進みます。

するとだいたい下図のようなページになります。

f:id:namakura878:20180819181709p:plain

左パネルの「SETUP -> Invocation」をクリックし、遷移した先のページで「OK Goolge, ○○○とつないで」の○○○にあたる部分を入力します。ここでは「HAL9000」としました。忘れずに右上の「SAVE」を押します。

これで新規プロジェクトの作成はできました。

Diagflowの設定

次に、どのようなセリフを受け取ったときにどのようなセリフを返すのかを決める「Diagflow」の設定をします。左パネルの「Build -> Actions」をクリックし、遷移した先のページで「ADD YOUR FIRST ACTIONS -> Custom intent -> BUILD」と進んでいきます。進んでいくと新しいタブが開かれます。この新しく開かれたタブで「Diagflow」を設定します。二回目以降にこのDiagflowを設定する場合は、「ADD YOUR FIRST ACTIONS」ではなく下図の赤丸で囲った「ADD ACTION」を押します。

f:id:namakura878:20180819181725p:plain

●Agent Nameと言語設定

「Agent Name」を設定し(初期の状態ですでに入力されている)、言語設定を日本語にし、「CREATE」を押します。ここの言語設定も後から追加できます。

インテントの設定

インテントでは、このアプリでどのような会話をするのかを設定します。左パネルの「Intent」で設定できます。

f:id:namakura878:20180819181738p:plain

デフォルトでは、

  • Default Fallback Intent(聞こえません等基本的なやりとり)
  • Default Welcome Intent(エージェント起動時に発する言葉等)

の二つが設定済みです。

●対応メッセージの入力

右上の「CREATE INTENT」を押します。初めに「Intent name」を設定します。「Contexts」や「Events」、「Action and parameters」の部分はいじらなくても大丈夫です。「Training phrases」に受け取るフレーズ(複数個でも可)を入力し、「Responses」に返されるフレーズを入力します。

f:id:namakura878:20180819181757p:plain

「Training phrases」にボーマン船長のセリフ、「Responses」にHALのセリフを入力しました。この「Responses」の部分では、SSMLが使えるので、audioタグを使ってHALのボイスを再生させます。mp3やoggファイルが使えますが、ソースのURLはhttpsの必要があるので注意してください。参考にさせていただいた記事ではGoogleのCloud Storageを使用されていました。

Diagflowの設定はこれでおしまいです。

アプリ情報を設定

実機で動かすにはアプリ情報を設定する必要があるので、次はそれを行います。Actions on Googleの画面から申請に必要なアプリ情報を設定していきます。

まずは左パネルの「DEPLOY -> Directory information」で必須の項目をすべて埋めて、右上の「SAVE」を押します。

次に「DEPLOY -> Location targeting」、「DEPLOY -> Surface capabilites」を設定します。「Surface capabilites」はすべてNoで大丈夫です。右上の「SAVE」も忘れずに押してください。

これでアプリ情報を設定はおしまいです。

シミュレータで試す

「TEST -> Simulator」でシミュレータを起動できます。ここである程度きちんと動いているかを試してみましょう。

f:id:namakura878:20180819181819p:plain

また、「Invocation」などのパラメータを変更した場合は、「CHANGE VERSION」を押さないと変更が反映されないので注意しましょう。

実機(スマートフォン)で試す

アプリを開発したのと同じアカウントで紐づけされているスマートフォンGoogle Homeなら、この段階で作成したアプリを起動できます。実際に動かしてみたのが下の動画となります。

正直ここまででだいぶ満足してしまったので続きをやるかどうかはわかりませんが、もし次回があればはRaspberry Piでガワを作ります。

参考記事