Rustで高階関数を使う
はじめに
高階関数は便利だ。
なので、rustでも使いたい。
よって、使い方を検証した。
下記にrust環境構築方法をまとめている。
dockerとvscodeでrust環境を構築する on Windows10 - イヌツムリのメモ
結論
引数になる関数をBox
でラップすると、関数を引数に取れる。
戻り値になる関数をimpl
を指定すると関数を戻り値に取れる。
dyn
のありなしで何が変わるのか調べる。 -> traitオブジェクトを引数に取るときはdynをつける。
(2021/04/07 追記)
関数を引数にとるサンプルコード
fn map(f: Box<dyn Fn(i32) -> i32>, a: &Vec<i32>) -> Vec<i32>{ let mut b = vec![]; for &num in a{ b.push(f(num)); } b } fn twice(x: i32) -> i32{ x + x } fn main() { let arry = &vec![4, 5, 3, 2]; let v4 = map(Box::new(twice),arry); println!("{:?}", v4); }
引数に渡すときはBox::new(関数名)
で渡せる。
受け取り側の関数定義はBox<dyn Fn(i32) -> i32>
で受け取れる。
(2021/04/07 追記)
dynをつけないと下記エラーでコンパイラに怒られる。
trait objects without an explicit
dynare deprecated
トレイトを引数にとるのでなく、トレイトオブジェクトを引数に取るので、ということらしい。
トレイトではどんな関数が実装されているか不明で、メモリ確保ができないから、と理解した。
オブジェクト化されていれば、内容が明確なのでメモリ確保が可能になる。
関数を返すサンプルコード
fn map(f: Box<dyn Fn(i32) -> i32>, a: &Vec<i32>) -> Vec<i32>{ let mut b = vec![]; for &num in a{ b.push(f(num)); } b } fn add_x(x: i32) -> impl Fn(i32) -> i32{ move |a: i32| -> i32{ a + x } } fn main() { let arry2 = &vec![4, 5, 3, 2]; let f = add_x(4); let v2 = map(Box::new(f),arry2); println!("{:?}", v2); }
関数を戻り値に指定するにはimpl
を指定する。
関数を変数に束縛する際に引数を与える場合、move
しないと与えた変数の所有権を移せない。
Boxよりもimplを使ったほうがよい?
(2021/04/07 追記)
dynはトレイトオブジェクトを返すときに明示するとわかった。
なので、add_xの戻り値は、implでなく下記のようにBox
fn add_x(x: i32) -> Box<dyn Fn(i32) -> i32>{ let f = move |a: i32| -> i32{ a + x }; Box::new(f) } fn main(){ let arry2 = &vec![4, 5, 3, 2]; let f = add_x(5); let v2 = map(f,arry2); println!("{:?}", v2); }
というか、下記のようにimpl使ったほうがスッキリとかける。
(i32をTに変えてるのは気にしないで...)
map()の宣言時や、mapの引数にtwiceを与えるときにBoxを使わなくて良いのでスッキリする。
ただ、dynがあとからrustに実装されたことを考えると、私の理解に間違いがあるように思う。
fn map<T>(f: impl Fn(T) -> T, a: &Vec<T>) -> Vec<T> where T: Copy + std::ops::Add<Output = T> + std::ops::Sub<Output = T> + std::ops::Mul<Output = T> + std::ops::Div<Output = T> { let mut b = vec![]; for &num in a{ b.push(f(num)); } b } fn twice(x: i32) -> i32{ x + x } fn main(){ let arry1 = &vec![4, 5, 3, 2]; let v4 = map(twice, &arry1); println!("{:?}", v4); }
dockerとvscodeでrust環境を構築する on Windows10
はじめに
windows10上で、rustのdocker containerを作成し、 vscode上で編集したrustのソースコードをビルドして実行するまでの手順を示す。
概要図を挿入したい
手順
- rustのdocker imageを取得する
- rustのdocker containerを作成する
- vscodeのextensionをインストールする
docker containerの作成
dockerの記事リンク
container作成のため、rustのdocker imageをdocker hubより入手する。 windows上で、powershellを用いて下記コマンドを実行する。
> docker pull rust
下記コマンドを用いて、rustのdocker imageをダウンロードできたことを確認する。
> docker images REPOSITORY TAG IMAGE ID CREATED SIZE rust latest 2f75dad0e7a5 4 weeks ago 1.28GB
pull
したときにTagを指定していないため、Repositoryにrust
、Tagにlatest
と表示される。
正しくダウンロードできていない場合は、Repositoryにrust
が表示されない。
imageをダウンロードできたら、containerを作成する。
docker run --name rust-env -it -d rust /bin/bash
vscodeのextension
Remote - Containers をインストールする。
vscode extensionの記事
rustプロジェクトの作成とビルド、実行
vscode上にあるメニューバーのTerminalからNew Terminal
を選択してrust-envのterminalを起動する。
起動したterminalパネルから、rust環境向けのディレクトリを掘っておく。
$ mkdir rust $ cd rust
rustのツールcargo
を用いてプロジェクトを作成する。
今回helloプロジェクトを作成する。
$ cargo new hello
helloディレクトリ以下にディレクトリ及びファイルが作成されていることを確認する。
$ ls hello Cargo.toml src $ ls hello/src main.rs
vscode上にあるメニューバーのFileから作成したプロジェクトを開く。 Open Folderから作成したhelloを選択する。
図を挿入したい
main.rsはデフォルトでmain.rs
というファイルが作成されている。
fn main() { println!("Hello, world!"); }
cargo
コマンドを用いて、helloプロジェクトのビルドとバイナリを実行することができる。
cargo build
すればビルドだけ実行できる。
ビルド済みでcargo run
すればバイナリ実行だけ行うことができる。
$ cargo run Compiling hello v0.1.0 (/home/dogsnail/rust/hello) Finished dev [unoptimized + debuginfo] target(s) in 3.23s Running `target/debug/hello` Hello, world!
最後に
dockerを用いてrustプロジェクトを作成し、バイナリを実行する手順を示した。 次回は、rustファイルの分割および、外部ファイルを用いる方法を説明する。
参考
配列を引数にするより、イテレータを使うほうがbashらしい
はじめに
bashで配列を引数に取るように書こうとすると難しいし、読みにくい。
なのでより簡単でbashらしい書き方を調べたので記録する。
(2021/02/01)追記
結論
結論、下記のようにwhile read
することで、変数名に引数が代入されて処理できる。
疑問: 複数の引数を処理したい場合はどう記述するか? -> やってみた(2021/02/01)
function my_func(){ while read -r 変数名 do 処理 done }
パイプでイテレータ処理する
bashらしい関数とは、パイプで渡される引数を逐次処理できる関数である。
配列でまとめて渡されるような関数はbashらしくない。
また、引数をmy_func( args )
のように渡す書き方もbashらしくない。
(そういうのがやりたいなら、pythonいいよ)
同じ処理を複数回実施する場合はパイプで渡して逐次処理するのがbashらしい書き方である。
例
#!/bin/bash function one_to_ten(){ echo 1 echo 2 echo 3 echo 4 echo 5 echo 6 echo 7 echo 8 echo 9 echo 10 } function add_one(){ while read -r VAL #注意 変数宣言なのでVALの頭に$をつけないこと。 do echo $(( VAL + 1 )) # expr $VAL + 1するより早いらしい done } one_to_ten | add_one
ご存知の通り、bashでは関数の戻り値はecho
で渡すことができる。
(標準出力が戻り値になる。catでも、grepでもなんでもいい)
今回は例として、one_to_ten
関数で1から10の数値をecho
する。
(seq 1 10
と同じ。)
逐次処理するミソはパイプを使うことにある。
one_to_ten
の出力値をパイプでadd_one
へ渡す。
(引数は標準入力で与えられる。パイプすることでパイプ左の標準出力をパイプ右の標準入力にできる。)
add_one
は渡された値に1足してecho
する。
パイプを使っていることにより、one_to_ten
がecho
するたびに、
add_one
に値が渡されるため、逐次1加算する処理を実行できる。
(なんてbashらしい処理なんだ!)
readを使っていることにより、入力待機が発生して実現できている?要勉強
イテレータともう1つ引数をひとつ取る場合
(2021/02/01)追記
疑問: 複数の引数を処理したい場合はどう記述するか?
ということでやってみた。
引数を増やす場合、参照する位置パラメータを増やせば良い。
疑問:複数のイテレータを扱うには?
#!/bin/bash function one_to_ten(){ echo 1 echo 2 echo 3 echo 4 echo 5 echo 6 echo 7 echo 8 echo 9 echo 10 } function add_x(){ X=$1 while read -r VAL do echo $(( VAL + X )) done } one_to_ten | add_one 10
参考
VirtualBoxの仮想マシンをまとめてexportする
スクリプト
バックアップを取りたいが何回もexport何回もするのがめんどいので下記スクリプトを使う
#! /bin/sh list=(`vboxmanage list runningvms | sed -e "s/^.*\"\(.*\)\".*$/\1/"`) for vm in ${list[@]} ; do vboxmanage controlvm ${vm} poweroff; vboxmanage export ${vm} -o ${vm}.ova; done
説明
list=(`vboxmanage list runningvms | sed -e "s/^.*\"\(.*\)\".*$/\1/"`)
vboxmanage list runningvms
は起動している仮想マシンの一覧を取得している。
sed -e "s/^.*\"\(.*\)\".*$/\1/"
は取得した一覧から仮想マシンの名前のリストを取得している。
(正直、sedはよくわからんので、代わりにcut -d" " -f1
でいい気がする。 )
sedの結果を配列としてlistへ代入している。
for vm in ${list[@]} ; do 処理 done
list配列から1つずつ値を取り出してvmという変数に代入している。
list配列から値を取り出すたびに処理を行う。
vboxmanage controlvm ${vm} poweroff;
仮想マシンが起動しているとexportできないのでシャットダウンしている。 (余談だが、controlvm poweroffは電源引っこ抜くのと同じ動作らしい(公式のリンクを貼る))
vboxmanage export ${vm} -o ${vm}.ova;`
仮想マシンをexportする。
-o
によってexportする仮想マシンに名前をつけることができる。
もうちょいきれいにスクリプト書いた
よりshellスクリプトらしく、パイプ処理で記述した。
run_vm
を書き換えれば、ipアドレスの一覧を取得したりできる。
#!/bin/sh function get_name_of_runningvms(){ vboxmanage list runningvms | cut -d" " -f1 } function run_vm(){ while read -r line do echo "$line" | xargs -I{} vboxmanage startvm {} --type headless done } get_name_of_runningvms | run_vm
tex環境の構築onWindows
はじめに
wordは嫌いだ。 編集しているうちに、挿入した画像がどこかおかしなところへワープしていく。 数式を挿入するのもだるい。 他人が編集したwordファイルの謎の線がどうやって書き込まれたのか分析するのが大変だ。 (線の入れ方もいろいろあってどれなのか区別がつかない) ドキュメントフォーマットも、いつまで立っても新規作成する人がいる。 そこで、フォーマットとコンテンツを区別し、どうやって編集したのかが文字列として記録に残せ、バージョン管理システムとも相性が良く、画像がワープしていかないテキストコンパイラを導入したい。 というわけで、texの環境を整えたのでメモを残す。 ホントはtexよりもmarkdownでhtmlにドキュメント残したい。
環境構築
インストール対象のリスト
tex環境を構築するには下記をインストールする (もう、書くことなくなった)
- エディタ: texmaker
- コンパイラ: uplatex
- epsプリンタ: ghost script, postscriptプリンタドライバ
- eps viewer: gsview
texmakerのインストール
https://www.xm1math.net/texmaker/download.html
設定
uplatex -no-guess-input-enc -kanji=utf8 -synctex=1 -src-specials -interaction=nonstopmode -jobname=% %.tex
なんかdviでプレビューするのは古いらしい quick buildでpdfまで作るように設定して、texmakerでpdfをプレビューする
手順
- uplatexのインストール
- texliveでインストールする
- 下記からisoファイルをインストール
- install batを起動する。
インターネットインストールにすると死ぬほど時間がかかる。 isoも死ぬほど時間がかかるが。
http://www.tug.org/texlive/acquire-iso.html
postscriptプリンタドライバのインストール
https://www.fujixerox.co.jp/download/apeosport/6_c7771/win10_64e/ps_w