Rustでアプリ
音声配信ビジネス
上記題名の本を借りてきた。今のトレンドらしい。何故って、おうちで仕事、YouTubeを見てたら仕事にならない。聴き流しなら大丈夫って訳。ラジコもいいけど、それじゃつまらんと言う需要が有るらしい。
ユーチューバーでやってくのは設備が大変、世間に顔出し出来る程、良い顔してない。でも、音声なら大丈夫。設備もスマホにアプリを入れるだけ。これで、私設のラジオのごとく拡散出来るってのが受けたんでしょうな。
聴く方は、
https://stand.fm/ (拡散は自分でRSSフィードするなり、ツイターでって方針みたい)
こういう所へ行って、好きな番組を選べば良い(中には有料も有るから気を付けて)。
ipadは長寿命
ipadをソファーから落としてしまった。画面が割れてお陀仏したかと思ったら、壊れたのはカバーだった。カバーが盾になって本体を守ってくれたのね。
このカバーは2代目だ。初代のやつはipadと同時に購入したイタリア製だったかのおしゃれな奴。それがボロボロになったので、日本製の安いやつに買い替えたんだった。
カバーが有るんで、炬燵に立てて使う事が出来たんだけど、カバー無しの本体だけだと、そうもいかない。困った時の100均頼りとばかりに、何か代用品が無いかと物色に出かけた。
そしたら有ったよ。針金を曲げ加工したタブレット置きスタンドが。なんか、段々とチープな物に変わって行くなあ。まあ、それでもいいんだけど。
このipad、使い始めてから8年目に突入する。電池がヘタって、途中で一度ぐらいは交換するかなあと思っていたけど、今の所至って元気だ。信じられないくらい。
この間、10年使った携帯を新しくした。この10年で、電池を4回も交換してるよ。それを思えば、ipadはよく出来てるな。電池がヘタるまで使って、2代目ipadは、アプル・シリコン入りのやつにしたいものだ。
そうそう、問題が一つ有った。長年の使用で充電器のケーブルのコネクタ近くで断線の兆候が有るんだ。そんな事もあろうかと、一応スリーブで保護はされているんだけど、そのスリーブが硬いため、ケーブルの曲げがスリーブの端に集中しちゃってる。保護テープでも巻いて、固定しておくかな。同じような事、誰かの携帯の充電器でも発生してたって言うから、以外なウィークポイントなんだな。
Rustの資料
ちまちまと資料集めをしている。本も買ってないし勉強会に出るわけでもないので、気が付いた時にメモしてるんだ。
程よい分量で、適切な例が載ってたので、よく分かった(つもり)。トレイト境界なんて説明が出てきてた。ちょっとコードを引用させてもらうと
// Tという型を用意し、TはCarを実装している型に限定する。
// Carを実装している型は、run()とrefuel()を実装していることが保証されるため、具体的な型名は問わない。
fn drive<T>(mut car: T, distance: u32) -> T
where
T: Car, // TはCarを実装した型に限定する → トレイト境界
{ ... }
Tはトラックとかスポーツカーとかのcar族に限定されるって宣言だな。同じ考え方がhaskellにも有ったな。
Prelude> :t sqrt sqrt :: Floating a => a -> a
sqrtは、aの型を受け取ってaの型を返す。aは浮動小数ねって制約。
Prelude> sqrt 10 3.1622776601683795 Prelude> sqrt 4 2.0
出力は必ず浮動小数になるって事だな。入力が整数であっても、これは浮動小数の一種って解釈してるんだな。
Rustに影響を与えた言語たち good
文字列でのcsv取り込み
前回CSVファイルのデータを読み込む方法を調べた。最終的には、読んだデータをgnuplot用に書き出すんで、文字列のまま処理したい。但し、起床時と就寝時に分解したい。 そんな訳で、下記のようにした。
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() -> std::io::Result<()> {
let mut am: Vec<Vec<String>> = Vec::new();
let mut pm: Vec<Vec<String>> = Vec::new();
for line in BufReader::new(File::open("aa.csv")?).lines() {
let ap = line?.split(',').map(String::from).collect::<Vec<_>>();
if &ap[0][6..7] == "0" {
am.push(ap);
} else {
pm.push(ap);
}
}
println!("{:?}", am);
Ok(())
}
am/pmの判定は、6文字目を見ると言う安易な方法なんだけど、これをap[0][6]のようにしちゃうと、怒られる。整数による文字列の参照は出来ませんですって。そんな訳で、スライスに変えた(実質1文字しか取り出していない)。全く、トラップが各所に仕込まれているな。
ファイルへの書き出し
昔のgo製プログラムでは、gnuplotへのデータ渡しにファイルを使ってた。そして、gnuplotのスクリプトもファイル渡ししてた。そんな訳で、今度は書き出しだ。
use std::io::{BufRead, BufReader, Write};
fn main() -> std::io::Result<()> {
:
dump4gp("am.dat", am);
dump4gp("pm.dat", pm);
:
}
fn dump4gp(dname: &str, seed: Vec<Vec<String>>) -> std::io::Result<()> {
const HOWLONG: usize = 3;
let mut file = File::create(dname)?;
for m in seed.len() - HOWLONG..seed.len() {
writeln!(file, "{} {} {}", seed[m][0], seed[m][1], seed[m][2])?;
}
file.flush()?;
Ok(())
}
constで宣言する変数名は大文字にするのが流儀らしい(debug中なんで3データ分にしてる。実戦では70データ分)。 そして、取り出すデータは直近の物って事で、(インデックスの)スライスを採用した。
これで動いた。けど
warning: unused `std::result::Result` that must be used
--> src/main.rs:17:5
|
17 | dump4gp("am.dat", am);
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_must_use)]` on by default
= note: this `Result` may be an `Err` variant, which should be handled
こんな注意が出てきてた。どうしろと?
それより、気になる記事を見つけた。バッファリングしながら書き出すと性能が4倍にあがるとな。
use std::fs::OpenOptions;
use std::io::{BufWriter, Write};
fn main() {
let file = OpenOptions::new()
.append(true)
.create(true)
.open("./sample.txt").expect("ファイル作成エラー");
let mut buf_writer = BufWriter::new(file);
for _ in 0..1_000_000 {
writeln!(buf_writer, "hello").expect("書き出しエラー");
}
}
see also: Rustで高速な標準出力 in Posts
有名な方に遭遇してしまったぞと。
gnuplot用のスクリプトを用意して、起動するぞ
次はgnuplot関係。goの時はgnuplotでも統計の計算を出来る事に気が付き、それを利用する事にした。但しスクリプトを本体と別にしちゃうと、シングルバイナリーにならないので、スクリプトをgoのアプリ内に保持しておいて、使う時に展開する事にしたんだった。
スクリプトファイルの自動作成
use std::fs::File;
use std::fs::OpenOptions;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::process::Command;
fn main() {
:
scr4gp("topdf.plt");
run4gp();
// std::fs::remove_file("topdf.plt");
}
main側での呼び出し関係とuse一式。stdだけを使うすっきり仕様だ。
fn scr4gp(sname: &str) {
let gsrc = "# blood graph for gnuplot
set encoding utf8
set terminal pdfcairo mono font \",20\" size 21cm, 29cm
set output \"./zzz.pdf\"
#set output \"Desktop\\\" . when . \".pdf\"
stats \"am.dat\" using 2:3
amh = sprintf(\" 最高血圧(平均=%.1f 偏差=%.1f)\", STATS_mean_x, STATS_stddev_x)
aml = sprintf(\" 最低血圧(平均=%.1f 偏差=%.1f)\", STATS_mean_y, STATS_stddev_y)
";
let file = OpenOptions::new()
.append(true)
.create(true)
.open(sname)
.expect("ファイル作成エラー");
let mut buf_writer = BufWriter::new(file);
writeln!(buf_writer, "{}", gsrc).expect("書き出しエラー");
}
gssrcってのは、gnuplot用スクリプトの一部だ。昔のgoのソースから引っ張ってきての書き込みテスト用だ。
スクリプトのソースは御多聞に漏れず、文字列を表すダブルクォーテーションが多用されてる。 rustcの文字列もダブルクォーテーションで囲む約束になってるので、文字列中のそれは、いちいちエスケープしないとならない。
goとかpythonとかは、文字列を囲む記号に、シングルクォーテーションを使えたりして便利。rustも是非、こういう気配り宜しく。静的解析で、文字列って判定は容易なはずなんで、任意の記号も採用出来るはず。後、ヒアドキュメントも検討宜しく。って、スクリプターからの強い要求だからね。
それから、何気にUTF-8の表現だけども、問題無かった。トラブルが出るかと(期待?)してたんだけどね。
gnuplotの実行
最後は、gnuplotを走らせる部分。goでどうなってたか、確認しとく。
func ag(datafile string) string { // for gnuplot option
:
return fmt.Sprintf("when=%s", line[:6])
}
exec.Command("gnuplot", "-e", ag("am.dat"), "topdf.plt").Run()
gnuplotにスクリプトを渡せばいいんだけど、-eで、コマンドも渡している。when=210110 みたいな奴。これスクリプト中へ渡す引数だ。出来上がるpdfファイル名に使ってる。 そんな事を思い出しながら、
fn run4gp() {
let rs = Command::new("gnuplot")
.arg("-e \"when=210110\"")
.arg("topdf.plt")
.output()
.expect("Failed to execute command");
println!("{:?}", rs);
}
引数が複数有る場合は、上記のように .arg(…) を重ねるか .args(&["-c", "hello"])のようにすれば良い。Struct std::process::Command
Module::processの例を見てて飽き足らず、Stuructsにある、
Command A process builder, providing fine-grained control over how a
new process should be spawned.
をダブルクリックして、案内された次第。
実行結果はrsに帰って来るので、表示してみた。
まずは、期待値
sakae@pen:/tmp/bld$ gnuplot -e "when=210110" topdf.plt * FILE: Records: 3 Out of range: 0 : Correlation: r = 0.2402 Sum xy: 2.201e+04
次は、rustcが作ったアプリから
sakae@pen:/tmp/bld$ target/debug/bld
Output { status: ExitStatus(ExitStatus(0)), stdout: "",
stderr: "unrecognized option -e \"when=210110\"\n\n* FILE: \n Records: 3\n Out of range:
:
r = 0.2402\n Sum xy: 2.201e+04\n\n" }
なんか、許されないオプションって言ってるけど何だろう。それを無視すれば、ちゃんとgnuplotとやり取り出来ているな。
.args(&["-e", "when=210110", "topdf.plt"])
指示の仕方が間違ってた。引数は、ちゃんと区切ってやらないと駄目みたい。
まとめ
ごちゃごちゃソースを載せてきたので、一本にしたものを載せておきます。
使う時は、main.rsにファイル名を変更して。stdモジュールしか使っていない清いやつです。
これをコンパイルして、同階層にcurrent.csvファイルを置いてください。実行結果は、zzz.pdfってやつになります。
whenに年月日の数値を与えるようにして、gnuplotスクリプトを少々改変すれば、ちゃんとした日付のpdfになります。面倒なんでやっていない。
go本が有ったのでrustの文法と比べているんだけど、rustの方がオイラーにはしっくり来るなあ。haskellと似てるからかな?
etc
なかなか良さげなまとめだなあ。何とか理解出来る頭になってきたぞ。
https://zenn.dev/topics/rust?page=1
この方も有名だな。あれ? 色々な人が寄稿してる。rustの集積場か。