スライシングでの表現(@Rust)

Beginning Rustからのコードですが、変数というのはある型の表現のaliasというのはRustでも言えることですが、そこから以下のrange = 2..5のような定義もできます。2..5のように表現される形式の型は、

std::ops::Range

になります。

https://doc.rust-lang.org/std/ops/struct.Range.html

さらに発展系とも言えますが、&[23, 17, 12, 16, 15, 2][2..5]のような表現も順序立てて見てみると有効になることがわかります。

最後の1行はその直前の4行と等価ですが、なぜ最後の1行のような表現ができるかがわかるプロセスそのものになっています。

fn main() {
    fn min(arr: &[i32]) -> i32 {
        // Let's assume 'arr' is not empty.
        let mut minimum = arr[0];
        for i in 1..arr.len() {
            if arr[i] < minimum { minimum = arr[i]; }
        }
        minimum
    }

    let arr = [23, 17, 12, 16, 15, 2];
    let range = 2..5;                       // a variable can accepts like "2..5"
    let slice_ref = &arr[range];
    print!("{}\n", min(slice_ref));

    // &[23, 17, 12, 16, 15, 2] is as &arr
    // &[23, 17, 12, 16, 15, 2][2..5] is as &arr[range]
    print!("same as above : {}", min(&[23, 17, 12, 16, 15, 2][2..5])); 
}

蛇足ながら、なぜスライス(可変長)を引数とするときには参照でなければならないかというと、コンパイル時に長さが決まっていないといけないから、他の型でも全てそうですが。本質的に引数や戻り値はスタック上で固定長で管理されるので、可変(任意)長では管理できないからという理屈です。

上記のコード例ではarrはサイズが可変長なので参照を使い、minimumはサイズが既知で固定なので戻り値として使用できることになります。

 

admin

 

PHPからSQLite3を使うための設定

Golangで作ったSQLite3のデーターベースをPHPからアクセスするためのメモです。初期状態ではSQLite3は使えないのでインストールと初期設定が必要です。

・実行環境と版数

OS : Raspberry PI zeroのRaspbian

$ php –version

PHP 7.3.31-1~deb10u7 (cli) (built: Jun 17 2024 21:48:38) ( NTS )

$ apachectl -v

Server version: Apache/2.4.38 (Raspbian)

 

・sqlite3 driverのインストール

$ sudo apt install php-sqlite3

インストール確認は、$ php -mで、

sqlite3 // これが見えればOK

 

・Apacheの再起動

これをしないとPHPコマンドからのスクリプト起動はうまくいくが、Apacheとはまだ連携していないのでブラウザ経由でSQLite3へのアクセスはできない。(しばらくハマった)

$ sudo service apache2 restart

ちなみに該当のコードは以下の通り、

<?php
$file = 'presenters_list.json';

if (file_exists($file)) {
    $json_data = file_get_contents($file);
    $presenters = json_decode($json_data, true);
    
    // ---------------------------------------------------
    // to access SQLite3 data (myfare app) and pull Ninja names who will make a presentaion
    $db_name = '/home/pi/myfare/myfare.db';
    $mifare = '';
    // to calculate a table name for the sqlite3 data base
    $year = date('Y');
    // leap year check
    if ($year%4 == 0){
        $show_date = ($year - 1).'-12-31';
    } else{
        $show_date = $year.'-1-1';
    }
    $show_date_timestamp = strtotime($show_date);
    $past_days = intval((abs(time() - $show_date_timestamp)) / (60 * 60 * 24));
    $tbl_name = "tbl".$year.$past_days;
    //$tbl_name = 'tbl2023108';   // tbl2023108 : this line is only for debugging
    //query names whose pressentation status is ON

    $db = new SQLite3($db_name);   
    $query_name = 'SELECT name FROM '.$tbl_name.' where presentation="1";';
    // to check if the table is available and issue query if exists
    $sql = "SELECT name FROM sqlite_master WHERE type='table' AND name="."'$tbl_name'";
    $result = $db->query($sql);

    if ($result->fetchArray()) {
        $result = $db->query($query_name);

        if (!empty($result)) {
            while ($row = $result->fetchArray()) {
                $mifare = $mifare.json_encode($row['name'].'@mifare').',';
            }
        }
        } else {}
    $db->close();
    // end of myfare data hundle code
    // ---------------------------------------------------
    
    if ($presenters !== null) {
        // to check if there is any name on presenter_list
        if (json_encode($presenters)== '[]'){
            echo '['.substr($mifare, 0, -1).']';
        }else{
            echo '['.$mifare.substr(json_encode($presenters), 1);
        }
        
    } else {
        echo json_encode(array('error' => 'error occured during file analysis'));
    }
} else {
    echo json_encode(array('error' => 'could not find the file'));
}
?>

 

admin

&strはポインタと長さのペアの情報を持つ(@Rust)

&strというのは単なる参照かと思ってましたが、参照ポインタと長さ情報を持つんですね。

Beginning Rustのサンプルコードを一部変更したものが以下で、M1 MacBook Airで実行した結果がコメントの数字になりますが、

fn main() {
    // all str consist of UTF-8
    use std::mem::*;
    let a: &str = "";
    let b: &str = "0123456789";
    let c: &str = "abcdè";
    print!("{} {} {}; ",
        size_of_val(&a),    // -> 16
        size_of_val(b),     // -> 10
        (c).len());         // -> 6
    print!("{} {} {}",
        size_of_val(&&a),
        size_of_val(&&b),
        size_of_val(&&c));
}

size_of_val(&a)の結果は16ということは、ポインタで8バイト、長さ情報情報で8バイト持つことになります。次のsize_of_val(b)は参照ではなく文字列そのものの長さなので10、(c).len())はsize_of_val(c)でも同じですが、6になります。

なぜなら、Rustでは文字の内部コードはUTF-8を使っていますがアクサン・グラーヴ(è)は2バイトなので、5ではなく6になります。

&strを使ってて普段意識することはそれほどないはずですが、知ってて損はない情報のように思います。

 

admin

MAMP

PHP環境のためにMAMP(M1 MacBook Air)を入れようとしたら、今は有償版もあるんだね。

 

この写真はクラシックな無償版だけれども、proの無償期間が切れたらどうなるんだろう?

MAMP立ち上げてHTTP起動したぐらいでは、メモリ使用量は100MB以下なので今にしては軽量級です、

 

admin

vec!のpop()戻りはOption(@Rust)

以下のコードがBeginning Rustに出てきますが、: Option<i32>はOption<T>の説明上出て来るだけで、コンパイラが型を推測できるからなくても良い。

Option<T>の説明だからあえて冗長に指定しているだけで、実際のコードで記述することはないだろう。

fn main() {

    let mut v = vec![11, 22, 33];
    for _ in 0..5 {
        let item: Option<i32> = v.pop();
        match item {
            Some(number) => print!("{}, ", number),
            None => print!("#, "),
        }
    }
}

vec!のpop()戻りは、以下のドキュメントにある通りOption<T>になります。

https://doc.rust-lang.org/std/vec/struct.Vec.html#method.pop

i32は明示的な指定ですが、ここはベクターの要素を満足できる型ならばu32でもu8でも良い。コンパイラは最適化をするだろうから、内部的にはu8で扱っているはず、と思ったチェックしてみたら実はi32でした。64bitマシン(MacBook M1 Air)であればu8よりもi32の方が効率的なのかも知れないね。

fn main() {
    let mut v = vec![11, 22, 33];
    for _ in 0..5 {
        let item = v.pop();
        p_typename(item);
        match item {
            Some(number) => print!("{}, ", number),
            None => print!("#, "),
        }
    }

    fn p_typename<T>(_: T) {
        println!("{}", std::any::type_name::<T>());
    }
    
}

で実行すると、

core::option::Option<i32>

と返ってきたから、

 

admin

char::from_u32(@Rust)

Beginning Rustの「基本データ」の第6章に0~255までのu8形式の整数を文字表示できる整数の範囲から表示するのに、以下のようなコードが出てきますが、

fn main() {
    for n in 32..127 {
        println!("{}: [{}]", n, n as u8 as char);
    }
    for n in 160..256 {
        println!("{}: [{}]", n, n as u8 as char);
    }
}

ここで、

n as u8 as char

の代わりに、

どの数(u8以外)でも一文字に変換したいときには、char::from_u32()を使えとあって、それを使うと以下のようなコードになります。

fn main() {
    for n in 32..127 {
        println!("{}: [{}]", n, char::from_u32(n).unwrap());
    }
    for n in 160..256 {
        println!("{}: [{}]", n, n as u8 as char);
    }
}

char::from_u32()関数の解説は、

https://doc.rust-lang.org/core/primitive.char.html#method.from_u32

にあるように戻りの形はSome()なので、中身を取り出すためにunwrap()を使っています。オリジナルよりもこの方が見やすいように思います、処理速度は遅くなりそうですが。

 

admin

懇切丁寧

Rustの標準のネット上のテキストもほぼ通しで読んでみたので、まとめとして本のテキストを買ってみた。

インプレス本なので、懇切丁寧と言える記述内容ですが、Rustの全領域をカバーしているわけではなくて基本的な部分をサンプルコードを使って完全習得というのを目的にしているようです。この一冊をクリアすれば、中級レベルの素材もそれほど無理なくこなせるというような内容でしょうか。

 

admin

 

 

 

フルパス指定(Fully qualified path)(@Rust)

https://doc.rust-jp.rs/rust-by-example-ja/trait/disambiguating.html

に出てくるコードで最後の4行は

<Type as Trait>::function(receiver_if_method, next_arg, …);

のようなフルパス指定ししなくとも

    let username = UsernameWidget::get(&form);
    assert_eq!("rustacean".to_owned(), username);
    let age = AgeWidget::get(&form);
    assert_eq!(28, age);

と書いて、Traitを指定するだけでもコンパイルできます。関数の引数に(&form)、つまりFormのインスタンスを使っているからだと思いますが。

一方関連関数new()を追加した以下のコードだと、

trait UsernameWidget {
    // Get the selected username out of this widget
    fn get(&self) -> String;
    fn new(name: String, age: u8) -> Self;
}

trait AgeWidget {
    // Get the selected age out of this widget
    fn get(&self) -> u8;
}

// A form with both a UsernameWidget and an AgeWidget
struct Form {
    username: String,
    age: u8,
}

impl UsernameWidget for Form {
    fn get(&self) -> String {
        self.username.clone()
    }

    fn new(str: String, age: u8) -> Self {
        Form {
            username: str,
            age: age,
        }
    }
}

impl AgeWidget for Form {
    fn get(&self) -> u8 {
        self.age
    }
}

fn main() {
    // both UsernameWidget & AgeWidget are implemeneted to Form, you can use Form::new() instead of  
    // the fully-qualified path <form as="" usernamewidget="">::new(...) 
    let form = Form::new(String::from("John"), 30); 
    let username = UsernameWidget::get(&form); 
    assert_eq!("John".to_owned(), username); 
    let age = AgeWidget::get(&form); assert_eq!(30, age); 
}

もし、

let form = Form::new(String::from("John"), 30);

let form = UsernameWidget::new(String::from("John"), 30);

とするとこれはコンパイルできない(use the fully-qualified path to the only available implementationのようなエラーが出る)、なぜなら関連関数(Associated function)だから構造体情報を持たないので対象の構造体を特定できないから。

しかしソースコードの最後のほうの行で、オリジナルソースのようにフルパスを指定してやれば、

let form = UsernameWidget::new(String::from("John"), 30);

でもコンパイルできる。これはコンパイラが構造体FormとトレイトUserWidgetの関連を他の行からの情報で知ることができるようになったからだろう。

構造体Formにアクセスするだけなら、Form::new(…..)を使うのが最もコンパクトで分かりやすいコードになりますが、これはimpl Trait for Structがあるから有効なコード。

フルパス記法については、

https://doc.rust-jp.rs/book-ja/ch19-03-advanced-traits.html

がオリジナルのテキストになります。

 

admin

スーパートレイト(@Rust)

Rustに継承はないですが、似たような機能ということであるトレイトの上位トレイトを定義可能です。

Trait_b: Trait_aのような記法でTrait_aがスーパートレイトでTarit_bはサブトレイトになります。

https://doc.rust-jp.rs/rust-by-example-ja/trait/supertraits.html

にサンプルコードがありますが、main()から呼べるようにしたのが以下のコードでGeminiで追加部分のコードをジェネレートしましたが、構造体CSStudentを定義して必要な関数を実装し、main()からは構造体のインスタンスを作成して関数comp_sci_student_greeting()を呼び出しています。

// Define a struct that implements CompSciStudent
struct CSStudent { 
    name: String,
    university: String,
    fav_lang: String,
    git_username: String,
}

trait Person {
    fn name(&self) -> String;
}

// Person is a supertrait of Student.
// Implementing Student requires you to also impl Person.
trait Student: Person {
    fn university(&self) -> String;
}

trait Programmer {
    fn fav_language(&self) -> String;
}

// CompSciStudent (computer science student) is a subtrait of both Programmer
// and Student. Implementing CompSciStudent requires you to impl both supertraits.
trait CompSciStudent: Programmer + Student {
    fn git_username(&self) -> String;
}

fn comp_sci_student_greeting(student: &dyn CompSciStudent) -> String {
    format!(
        "My name is {} and I attend {}. My favorite language is {}. My Git username is {}",
        student.name(),
        student.university(),
        student.fav_language(),
        student.git_username()
    )
}

impl Person for CSStudent {
    fn name(&self) -> String {
        self.name.clone()
    }
}

impl Student for CSStudent {
    fn university(&self) -> String {
        self.university.clone()
    }
}

impl Programmer for CSStudent {
    fn fav_language(&self) -> String {
        self.fav_lang.clone()
    }
}

impl CompSciStudent for CSStudent {
    fn git_username(&self) -> String {
        self.git_username.clone()
    }
}

fn main() {
    let bob = CSStudent {
        name: "Bob".to_string(),
        university: "Cambridge".to_string(),
        fav_lang: "Ruby".to_string(),
        git_username: "claw".to_string(),
    };

    println!("{}", comp_sci_student_greeting(&bob));
}

ここで&dyn CompSciStudentの記法がありますが、トレイトオブジェクトと呼ばれる動的ディスパッチです。

dyn prefix(@Rust)

これはコンパイル時にサイズが決定できないので&dynを使い、またdynはfat pointerです。さらに&dyn CompScistudentはimpl CompScistudentでもコンパイル可能で、impl Traitで静的に呼び出しもできます。静的指定に比べると多少なりとも負荷は増加しますが、

https://isehara-3lv.sakura.ne.jp/blog/2024/06/04/impl-traitはstaticであるrust/

 

admin

Visual Scripting(Unity)

Visual Scriptingは最近のゲームエンジンではかなり標準のようで、Unityのtutorialにも紹介されています。

https://learn.unity.com/project/roll-a-ball-visual-scripting-jp?uv=2021.2

Mx MacでもUnityはサポートされているので、動かしてみましたが、ゲーム版のノーコード作成ツールですねこれは。

<環境>

・M1 Mac Air : メモリ16GB

おそらくメモリ8GBだと辛いと思う、16GBでもメモリは12GB程度消費して、CPUリソースは30%ぐらい消費してパームレストは熱くなっていたから。ちなみにintel MacのVMware上のUbuntuではメモリ不足(8GB割り当て)のせいか途中でノーレンスポンス状態、

・Unity version : 2022.3.32f1

tutorialに物理条件(Physic Material)追加して、落下して粘土のようにプレートに付着するのでボールのように自然に跳ね返りするようにしましたがこれもノーコードで実現可能。

 

<Visual Scriptingのサンプル>

Itemの消滅対応のスクリプトですが、On Trigger Enterでcollisionを検出して、collision先がPlayerであればIfブロックでTrue出力してThis(自分自身)をDestroyするという流れになっているのが分かります。この程度だと分かりやすいけれども、ロジックが複雑化すると画面に納まりきらなそうなのは、たとえばScratchのようなブロック言語でも同じ。

Visual Scriptingで検索すると、Unityに限らず他のゲームエンジンでも実装されているようなので、ゲームは開発を役割分担しているはずなのでプログラマー以外のたとえばデザイナーにはこのようなツールは必需品とも言えるのではないかと思います。

 

admin