Rustのsplit_atで日本語を使う時の注意点
O'ReillyのProgramming Rust Chapter 3: Basic TypesのTuplesに出てくる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バイトであり、2
はchar
の境界ではないと言っている。
結論
日本語を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 }
という関数において、戻り値の型から、戻り値
x
がi32
となるのは自明なので、fn sample2() -> i32 { let x = 100; x }
と書ける。
Rustでは、Cで
int
とlong
に相当する型はそれぞれi32
とi64
、float
とdouble
に相当する型はそれぞれf32
とf64
になっている。i32
とi64
のどちらにも推論できる場合はi32
となる。それ以外の場合は、曖昧さをエラーとして報告する。f32
とf64
のどちらにも推論できる場合はf64
となる。それ以外の場合は、曖昧さをエラーとして報告する。
本題
Chapter 3. Basic TypesのFloating-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());
としてみたが、これも先ほどと同様のエラーが出てしまう。
let y = 1.0; // yはf64型を持つ
と書かれているので、上記のような書き方でも問題はないはずである。
結論
ここやここをみる感じでは、どうやらこの現象はrustcのバグっぽい…?
また、{float}
だけでなく{integer}
でも起こる場合がある模様。
参考文献
Rustの関数の引数でmutを使ったときの挙動
Rustの変数はデフォルトでイミュータブルである。たとえば、
let x = 10; x = 20;
はエラーとなる。値を更新したい場合は、
let mut x = 10; x = 20;
とする必要がある (mut
はmutableの略) 。
本題
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」で新規プロジェクトを作成します。
Project Nameは英字で決め(ここではHAL9000とした)、"Choose the default language for your Actions"でJapanese、"Choose your country or region"でJapanを選びます。言語設定は後からでも追加できます。
"Welcome to your project, HAL9000!"というページが出てくるので、「skip」で次に進みます。
するとだいたい下図のようなページになります。
左パネルの「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」を押します。
●Agent Nameと言語設定
「Agent Name」を設定し(初期の状態ですでに入力されている)、言語設定を日本語にし、「CREATE」を押します。ここの言語設定も後から追加できます。
●インテントの設定
インテントでは、このアプリでどのような会話をするのかを設定します。左パネルの「Intent」で設定できます。
デフォルトでは、
- Default Fallback Intent(聞こえません等基本的なやりとり)
- Default Welcome Intent(エージェント起動時に発する言葉等)
の二つが設定済みです。
●対応メッセージの入力
右上の「CREATE INTENT」を押します。初めに「Intent name」を設定します。「Contexts」や「Events」、「Action and parameters」の部分はいじらなくても大丈夫です。「Training phrases」に受け取るフレーズ(複数個でも可)を入力し、「Responses」に返されるフレーズを入力します。
「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」でシミュレータを起動できます。ここである程度きちんと動いているかを試してみましょう。
また、「Invocation」などのパラメータを変更した場合は、「CHANGE VERSION」を押さないと変更が反映されないので注意しましょう。
実機(スマートフォン)で試す
アプリを開発したのと同じアカウントで紐づけされているスマートフォンやGoogle Homeなら、この段階で作成したアプリを起動できます。実際に動かしてみたのが下の動画となります。
正直ここまででだいぶ満足してしまったので続きをやるかどうかはわかりませんが、もし次回があればはRaspberry Piでガワを作ります。