イヌツムリのメモ

学習したことのメモである。しかし、他の人が読んでもわかるように書こう。

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 explicitdynare 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のソースコードをビルドして実行するまでの手順を示す。

概要図を挿入したい

手順

  1. rustのdocker imageを取得する
  2. rustのdocker containerを作成する
  3. 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ファイルの分割および、外部ファイルを用いる方法を説明する。

参考

  1. Docker再入門 - Qiita

  2. Hello, Cargo! - The Rust Programming Language

配列を引数にするより、イテレータを使うほうが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_tenechoするたびに、 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

参考

  1. プログラマーの君! 騙されるな! シェルスクリプトはそう書いちゃ駄目だ!! という話 - Qiita
    素晴らしい記事だった。

  2. シェルで変数のインクリメントに expr を使うと100倍遅い件 - Qiita

VirtualBoxの仮想マシンを複数台まとめて起動する

複数起動したいとき

dogsnailという名前を含む仮想マシンをすべて起動するには下記のようなコマンドで実現できる。
grep以前を調整すれば応用可能

vboxmanage list vms | grep dogsnail  | cut -d" " -f1 | xargs -I{}  vboxmanage startvm {} --type headless

関連

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

MACアドレスを変更せずにVirtualBoxの仮想マシンをインポートする

virutalboxでは仮想マシンをインポートするとmacアドレスが変わる。
しかし、インポートの際に、macアドレスを維持したいことが多々ある。
理由としては、macが変わってしまうと、/dev ~のファイルやらなんやら設定し直すことになってめんどいからである。
のでインポートする際に--options keepallmacsをつける。
vmfile.ovaをインポートする例を下記に示す。

$ vboxmanage import vmfile.ova --options keepallmacs

(以前に本内容を書いたが、雑多だったので独立させた)

tex環境の構築onWindows

はじめに

wordは嫌いだ。 編集しているうちに、挿入した画像がどこかおかしなところへワープしていく。 数式を挿入するのもだるい。 他人が編集したwordファイルの謎の線がどうやって書き込まれたのか分析するのが大変だ。 (線の入れ方もいろいろあってどれなのか区別がつかない) ドキュメントフォーマットも、いつまで立っても新規作成する人がいる。 そこで、フォーマットとコンテンツを区別し、どうやって編集したのかが文字列として記録に残せ、バージョン管理システムとも相性が良く、画像がワープしていかないテキストコンパイラを導入したい。 というわけで、texの環境を整えたのでメモを残す。 ホントはtexよりもmarkdownでhtmlにドキュメント残したい。

環境構築

インストール対象のリスト

tex環境を構築するには下記をインストールする (もう、書くことなくなった)

  1. エディタ: texmaker
  2. コンパイラ: uplatex
  3. epsプリンタ: ghost script, postscriptプリンタドライバ
  4. 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をプレビューする

手順

  1. uplatexのインストール
  2. texliveでインストールする
  3. 下記からisoファイルをインストール
  4. 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

ghost script, gsviewerのインストール

http://pages.cs.wisc.edu/~ghost/index.htm