mapをやろう

リストをリストにmapする。
プログラミングの鍵は条件分岐
[b][size=150][b][size=150]このワークシートは[url=https://www.geogebra.org/m/twxxx3yq]Math by Code[/url]の一部です。[br][/size][/b][br]<mapをやろう>[br][/size][/b][br]前回の関数型プログラミングの導入では、[br]反復のfor[code][/code]条件を手続き型から宣言型に移[br]し替えることを学んだね。[br][br]「n for n in 数の範囲」で数nの「[color=#0000ff][b][size=150]リスト[/size][/b][/color]」ができた。geogebraではSequenceコマンドとして作れた。[br][br]「2*n+1 for n in 数の範囲」で数nの式を作ると、奇数の「[color=#0000ff][b][size=150]リスト[/size][/b][/color]」が作れる。[br][br]関数型プログラミングでは[br]もちろん、[br][color=#0000ff][b]sum[/b][/color](リスト)とすれば、リストの数値の合計が出せる。[br][color=#0000ff][b]find[/b][/color](リスト, "a")などでリストから要素の位置を求めたり、[br]concat(リスト、リスト)などでリスト連結をしたり、[br]push(リスト、a)などでリストに要素を追加したりすることはよくあることだね。[br][br]それだけではなない。[br][br][b][color=#0000ff]map[/color][/b](x->f(x),リスト)などとかいて、要素xからf(x)のリストをマッピングできる。[br][color=#0000ff][b]filter[/b][/color](x->expr(x),リスト)などとかいて、要素xのうちexpr(x)が成り立つ要素だけリストができる。[br][br]前回は乱数の行列を使って、200行5列の得点表を作ったね。[br]今回は乱数の2次元配列を作って、[br][br][color=#38761d][size=150][b]50行5列の得点表を作り、[br]平均点以上なら+、 未満ならーにマッピングしたい。[br][/b][/size][/color][br]juliaでリストのmapやってみよう。[br]2次元配列全体に対してはマッピングできない。[br]だから、[br]1行のデータリストdata[y]にまで入り込んで、[br]小数得点aveと比べるために得点を小数化したfloat(x)にし、[br]+-を返す関数updown(x)を作っておき、data[y]を+ーデータとしてmapしよう。[br][b][IN]julia[/b][br]#============================================[br][color=#0000ff][br]num = 50[br]kamoku = 5[br][b]data=[[rand(0:100) for x in 1:kamoku] for y in 1:num][br]ave= sum([sum(data[y]) for y in 1:num])/(num*kamoku)[br]updown(x) = float(x) >= ave ? '+' : '-'[br]res = [ map(x->updown(x) , data[y]) for y in 1:num][br][/b]println(res)[br][/color][br]#============================================[br][OUT]例[br][['-', '-', '+', '+', '-'], ['-', '-', '+', '+', '-'], ['-', '+', '+', '+', '-'], ['+', '+', '-', '+', '+'], ['-', '-', '+', '-', '-'], ['-', '-', '+', '-', '+'], ['+', '-', '-', '+', '+'], ['-', '-', '-', '+', '+'], ['-', '+', '-', '-', '-'], ['+', '+', '+', '+', '+'], ['+', '-', '+', '+', '+'], ['+', '-', '-', '+', '+'], ['+', '-', '-', '-', '-'], ['-', '-', '-', '-', '+'], ['+', '-', '+', '+', '+'], ['+', '-', '+', '+', '+'], ['+', '+', '+', '+', '+'], ['-', '-', '-', '-', '-'], ['-', '+', '-', '+', '-'], ['-', '-', '+', '+', '+'], ['+', '-', '+', '+', '+'], ['-', '+', '+', '-', '+'], ['-', '+', '-', '-', '-'], ['-', '+', '-', '+', '-'], ['-', '-', '+', '-', '-'], ['+', '+', '-', '+', '-'], ['+', '+', '+', '+', '+'], ['-', '-', '+', '-', '-'], ['-', '-', '-', '-', '+'], ['-', '+', '+', '-', '+'], ['+', '+', '-', '-', '-'], ['+', '+', '-', '+', '+'], ['+', '+', '-', '+', '+'], ['+', '-', '+', '-', '-'], ['-', '-', '-', '-', '+'], ['+', '+', '+', '-', '-'], ['-', '+', '+', '+', '-'], ['+', '+', '-', '-', '-'], ['-', '+', '+', '+', '-'], ['-', '-', '+', '-', '-'], ['+', '-', '-', '+', '-'], ['+', '-', '+', '-', '-'], ['-', '+', '+', '-', '-'], ['+', '-', '-', '+', '-'], ['-', '-', '+', '-', '+'], ['-', '+', '-', '+', '-'], ['+', '+', '-', '-', '+'], ['+', '+', '+', '-', '-'], ['+', '-', '+', '+', '-'], ['-', '-', '-', '+', '+']][br][br]残念だが、geogebraにはmapコマンドそのものはない。[br]しかし、if文は使えるので、updown(x)という3項演算子のような関数は使わずに、[br]直接sequenceコマンドにifを入れ込んでみよう。[br]データ要素と平均を比べる場面では、Elementコマンドを使うとインデックス指定で[br]データが取り出せるね。[br][b][IN]geogebra[/b][br]#============================================[br][color=#0000ff][br]num = 50[br]kamoku = 5[br][b]data=Sequence(Sequence(RandomBetween(0,100),n,1,kamoku),m,1,num)[br]ave=((Sum(Sequence(Sum(data(n)),n,1,num)))/(num kamoku))[br]rs=Sequence(Sequence(If(Element(data,m,n)≥ave,"+","-"),n,1,kamoku),m,1,num)[br][/b][/color][br]#============================================
[b][size=150][u][color=#9900ff]質問:rustを使って、関数型のコードはかけますか。[/color][/u][/size][br][/b][br]はい。かけます。[br]関数型も手続き型もどっちもいけます。[br][br]rustは最後のセミコロンあるものが文でないと式になります。[br]しかも、if構文はif式なので、[br]let A= ifナントカ;[br]という文で、変数Aにifで判断した結果をセットできます。[br]また、python,juliaのリストにあたるのがVectorです。固定サイズの配列とちがって要素の追加・削除が[br]できます。それに,python,julia,haskellのように関数型でかけるのに、インデントの場所は自由にできます。[br]しかし、[b]haskellとちがって、ふつうに反復構文(while, loopとか)が使える[/b]ので、[br][u]純粋な関数型言語とくらべると、自由度が高い[/u]ですね。[br]ただし、スピードとメモリの安全性を優先するので、型をできるだけ宣言しておきましょう。[br][br]rustのインストール方法はご自身で検索してください。[br][br]注意点は、[b]パッケージの更新がとても速いので、バージョンがすぐにあがること[/b]。[br]コマンドラインから[br][b]cargo new lst[br]cd lst[br][/b]とします。VScodeなどでlstフォルダを見ましょう。[br]乱数を使うためパッケージ(rustではクレートという)の記入しておきます。[br]Cargo.tomlの依存ファイルのところにパッケージ名="バージョン"を追加しておきます。[br]0.9.2が2025の1月で最新ですが、今後上がるでしょう。それにあわせて、書きましょう。[br]最悪、それにともなって、関数名まで古くなって新品に交換しないと動かなくなるかもしれません。[br][b][dependencies][br]rand = "0.9"[br][/b][br]次に、srcフォルダにあるmain.rsの中身を次のように貼りかえましょう。[br][color=#0000ff][size=150]//[IN]rust[br]use rand::{Rng};//乱数を使うためのクレートと関数をかく[br][br]fn main() {[br]    //50行5列の0以上100以下の乱数の得点表を作る。[br]    //平均点以上なら+、 未満ならーにマッピングしたい。[br]    let num = 50;[br]    let kamoku = 5;[br]    let mut rng = rand::rng();//乱数のもとを作る。[br]    //データ作成***************************************[br]    let data:Vec< Vec < i32 > > =[br]    (0..num).map(|_|[br]        {(0..kamoku).map(|_| rng.random_range(0..=100)).collect()} [br]            //kamoku数だけ発生してあつめる。 [br]        ).collect();                                               //num数だけ発生してあつめる。[br]    let total: i32 = data.iter().map([br]        |row| row.iter().sum::() // 科目合計[br]        ).sum();                                 // 合計の合計[br][br]    let ave = total as f64 / (num * kamoku) as f64;//平均の計算[br]    let updown = |x: i32| -> char { if (x as f64) >= ave { '+' } else { '-' } };[br]    let table: Vec< Vec < char > > =[br] data.iter().map(|row|[br]        {row.iter().map(|&x| updown(x)) .collect()}//行の要素を文字にする[br]        ).collect();//各行あつめる。[br]    println!("{:?}",table);[br]}[br][/size][/color]
出力は、 [br]コマンドラインにコンパイルと実行を両方やり、メッセージを最小にすると、juliaと同様の結果になります。[br][b]cargo run --quiet[/b][br][['-', '-', '-', '-', '+'], ['-', '-', '+', '+', '-'], ['+', '-', '+', '+', '-'], ['-', '+', '-', '-', '-'], ['-', '-', '-', '-', '+'], ['-', '+', '-', '-', '+'], ['-', '-', '+', '+', '+'], ['+', '-', '-', '+', '+'], ['+', '+', '+', '-', '-'], ['+', '-', '-', '+', '+'], ['+', '+', '-', '+', '+'], ['+', '+', '+', '+', '+'], ['-', '-', '+', '+', '-'], ['-', '-', '+', '-', '+'], ['+', '-', '-', '-', '+'], ['-', '-', '+', '-', '+'], ['-', '+', '-', '-', '-'], ['-', '-', '+', '-', '-'], ['+', '+', '+', '+', '+'], ['[br][br]rustの構文の特徴を確認しておきましょう。[br][br]・juliaでは、f(x) for x in リスト、f(x) for x in 範囲、というように[b]要素の操作のあとに対象[/b]をかきました。[br]rustでは、[br][color=#0000ff][b]範囲.map(|x| f(x))とか、[br]リスト.iter().map(|x| f(x)}というように、[br]対象のあとに操作[/b]をかきます。[br][/color]また、rubyのように[b]対象となる要素xには|x|のように、絶対値マークをつけます[/b]。[br]それに、対象要素xを不定マーク(_)にすることができます。すると、[br]範囲.map(|_| f(x))は、f(x)を範囲となる回数を繰り返すという意味になります。[br][br]また、注意しなければならないのは、[br]juliaではmapですぐにリストができたのに、[br]pythonではlist()でかこまないとリストができなかったと同じように、[br]操作した[b]結果をあつめてリストにしますよという指示、.collect()[/b]をつけないといけないということです。[br][br]map()とか、sum()とかで対象をかこむjuliaの書き方とちがい、[br]リスト[b].iter().[/b]map(要素と操作).collect()[br]リスト.[b]iter().[/b]sum(要素と操作)[br]のようにイテレーション(走査)の機能として末尾に命令をたしてく形式です。[br]書き方が逆ですね。[br][br]また、[b]rust[/b]は,[br][b][color=#0000ff]let 変数::型名=式;[br][/color][/b]という形で変数を型名を明確にして束縛します。[br]メモリを確実に確保するためです。[br]もちろん、[b]型推論[/b]という機能もあるので、忘れても平気なときもありますが、[br]変数を組み合わせて演算していくうちに、型を変換する羽目に陥るときがあります。[br]そのときは、[br][color=#0000ff][b]変数 as 変換先の型名[/b] [br][/color]という書き方をしてください。[br]変換しないと仕方ないときがあります。[br][b]整数A÷整数Bは整数の範囲の商[/b]になってしまうので、[br][b]A as f64 / B as f64[/b]のようにして商が小数になるようにしましょう。[br]大きいサイズを小さく変換すると[br]内容によっては変わってしまうこともあるので、[br]プログラムは動いても、内容がおかしくなることもありうるので注意しましょう。[br]また、一般的にrustの書籍は先端を進んでいるという感じの方がかいているので、[br]硬い用語をそのまま、あえて使っていることが多い気がします。[br]pythonよりはまだ一般化してないからです。[br]そういうものだという気持ちで情報と接することが大切ですね。[br][br]また、[b]プリント文にも注意[/b]しましょう。[br]!マークは関数ではなくマクロというものだという目印です。[br]それは約束事だからいいいのですが、{:?}が気になりますね。[br]tableは表だから、ベクターのベクターです。[br]だから、その[b]型名にあう出力[/b]が必要になりますが、とりあえずデータを押し出したいときは[br]{}ではあなく、[b]{:?}[/b]にすると形の崩れたままでよければ出力できる便利な指定です。[br][b][color=#0000ff]println!("{:?}",[/color][/b]table);[br][br]

Information: mapをやろう