今日の目標
今日は、複数の値をひとまとまりにして扱う tuple を学ぶ。
これまで変数には数値や文字列を1つずつ入れてきたが、実際のプログラムでは「商品名と価格」「名前と点数」「計算結果と判定結果」のように、関係のある値をまとめて扱いたい場面が多い。
そこで tuple を使い、複数の値を1つのまとまりとして保存する方法を身につける。
さらに、関数の戻り値として2つの値を返す書き方を学び、最後に商品名と価格をまとめて表示する小さなプログラムを作れるようになることを目標にする。
今日の内容
9ー1 ~tupleは「複数の値をまとめる箱」~
プログラムを書いていると、1つの情報だけでは意味が足りない場面がある。
たとえば商品を扱うなら、商品名だけでは不十分である。価格も一緒に扱いたくなる。
名前と点数、横幅と高さ、成功したかどうかとメッセージなど、実用的なデータは複数の値が組になっていることが多い。
このようなときに使えるのが tuple である。
tuple は、複数の値を1つの変数にまとめる仕組みである。たとえば、商品名 "りんご" と価格 120 を1つの変数 item にまとめられる。
let item = (name: "りんご", price: 120)
これで item には、名前と価格がセットになった情報が入る。
値を取り出すときは、次のようにドット . を使う。
echo item.name
echo item.price
ここで重要なのは、name や price は文字列ではなく、tuple の中の項目名であるという点である。
item.name は「item の中にある name の値を取り出す」という意味である。
よくあるミスは、項目名を間違えることである。
たとえば item.price と定義したのに item.cost と書くと、Nimは cost という項目を見つけられない。
初心者がよく見る「undeclared field」や「field not found」に近いエラーは、このような名前の不一致で起きる。
変数名と同じく、tupleの項目名も正確に書く必要がある。
また、文字列と数値を結合して表示するときにも注意が必要である。Nimでは、文字列と整数をそのまま & でつなげることはできない。
価格のような整数を文章に入れる場合は、$ を使って文字列に変換する。
echo item.name & "は" & $item.price & "円です"
$item.price は、整数である item.price を表示用の文字列に変換している。
型変換を忘れると、文字列結合のところでエラーになる。
9ー2 ~tupleは型として書くこともできる~
tuple は値をまとめるだけでなく、「このtupleはどんな項目を持つのか」を型として書くこともできる。
先ほどの商品であれば、次のように書ける。
let item: tuple[name: string, price: int] = (name: "りんご", price: 120)
このコードでは、item は name という文字列と、price という整数を持つtupleである、と明示している。
型を明示する理由は、プログラムを読む人にもNimにも、データの形をはっきり伝えるためである。
小さなコードでは型を書かなくてもNimが推測してくれることが多い。
しかし、学習段階では型を明示したほうが「この変数には何が入っているのか」を理解しやすい。
tupleの型を書くときは、項目名と型をセットで書く。
tuple[name: string, price: int]
これは「name は string、price は int である」という意味である。
ここで string は文字列、int は整数を表す。
よくあるミスは、値の型と宣言した型が合っていないことである。たとえば、次のようなコードはよくない。
let item: tuple[name: string, price: int] = (name: "りんご", price: "120")
price は int と宣言しているのに、実際には "120" という文字列を入れている。120 と "120" は見た目が似ているが、Nimでは別の型である。
計算に使う数値なら 120、文章として扱うなら "120" と考える必要がある。
もう1つの注意点は、項目の順番と名前である。
名前付きtupleでは、name や price のような項目名を使って値にアクセスできる。
初心者のうちは、名前付きtupleを使うほうが読みやすい。
item[0] のように番号で考えるより、item.name と書いたほうが意味が明確である。
9ー3 ~関数はtupleを使って複数の戻り値を返せる~
これまでの関数は、1つの値を返すものとして考えてきたかもしれない。たとえば、2つの数を足して合計を返す関数は、戻り値が1つである。
しかし実際には、関数から2つ以上の情報を返したいことがある。
たとえば、税込価格を計算するときに「税抜価格」と「税込価格」を両方返したい場合がある。
あるいは、入力チェックをするときに「成功したかどうか」と「メッセージ」を同時に返したい場合もある。
このようなとき、Nimでは戻り値にtupleを使える。
proc calcPrice(price: int): tuple[base: int, taxIncluded: int] =
let taxIncluded = price + price div 10
return (base: price, taxIncluded: taxIncluded)
この関数は、税抜価格 price を受け取って、税抜価格と税込価格を両方返す。
戻り値の型は tuple[base: int, taxIncluded: int] である。
関数の中では、return 文で tuple を返している。
let result = calcPrice(1000)
echo result.base
echo result.taxIncluded
関数を呼び出すと、戻り値のtupleが result に入る。
そこから result.base や result.taxIncluded として値を取り出せる。
よくあるミスは、戻り値のtuple型と return するtupleの項目名がずれることである。
関数の戻り値を tuple[base: int, taxIncluded: int] と書いたなら、return でも (base: ..., taxIncluded: ...) のように対応した項目を返すのが分かりやすい。
また、Nimでは割り算にも注意が必要である。div は整数の割り算である。
たとえば price div 10 は、整数として10で割った結果を返す。
税込価格の計算を厳密に扱う場合は小数や丸めの話も必要になるが、今回は初心者向けとして、整数計算で扱う。
課題1
この問題では、名前と点数をtupleでまとめる。
student という変数に、名前 "アリス" と点数 85 を入れ、最後に アリスの点数は85点です と表示する。
穴埋め箇所 ____ に正しいコードを入れて、プログラムを完成させること。
課題コードは以下の通り。
let student = (name: ____, score: ____)
echo student.name & "の点数は" & $student.score & "点です"
期待する出力は次の通り。
アリスの点数は85点です
ヒントとして、name には文字列を入れるので、値をダブルクォーテーションで囲む必要がある。
score には整数を入れるので、ダブルクォーテーションで囲まない。
表示するときは、student.score が整数なので、すでにコード内で $student.score と書いて文字列に変換している。
課題2
この問題では、出発コードを改造して、商品名と価格に加えて、税込価格も表示する。出発コードでは商品名と価格だけを表示している。
これを改造し、りんごは税抜120円、税込132円です と表示すること。税込価格は、価格に価格の10分の1を足して求める。
出発コードは以下の通り。
let item = (name: "りんご", price: 120)
echo item.name & "は" & $item.price & "円です"
期待する出力は次の通り。
りんごは税抜120円、税込132円です
ヒントとしてまず、税込価格を入れる変数を作るとよい。
整数の10分の1を求めるには item.price div 10 を使える。
つまり、税込価格は item.price + item.price div 10 で計算できる。
最後に表示するときは、item.price も税込価格の変数も整数なので、文字列とつなげる前に $ を付ける必要がある。
課題3
この課題では、関数を使って商品情報を作る。
入力は、商品名 "ノート" と価格 300 である。
処理では、関数 makeProduct を作り、商品名と価格をtupleとして返す。
出力では、関数から受け取ったtupleを使って、商品: ノート / 価格: 300円 と表示する。
出発コードは以下の通り。____ の部分を埋めて、動くプログラムにすること。
proc makeProduct(): tuple[name: string, price: int] =
return (name: ____, price: ____)
let product = ____
echo "商品: " & product.name & " / 価格: " & $product.price & "円"
期待する出力は次の通り。
商品: ノート / 価格: 300円
よくあるエラーは、関数を呼び出さずに代入してしまうことである。
let product = makeProduct
この書き方では、関数を実行して戻り値を受け取る形になっていない。
今回必要なのは関数の戻り値なので、次のように () を付けて呼び出す。
let product = makeProduct()
また、価格表示で $ を忘れるミスも多い。
echo "商品: " & product.name & " / 価格: " & product.price & "円"
これでは product.price が整数のまま文字列とつながろうとしてエラーになる。
価格を文字列に変換するために、$product.price と書く必要がある。
echo "商品: " & product.name & " / 価格: " & $product.price & "円"
今日のまとめ
今日は、複数の値をまとめる tuple を学んだ。
tuple を使うと、商品名と価格、名前と点数のような関係のある値を1つの変数として扱える。
値を取り出すときは item.name や item.price のようにドットを使う。
tuple の型は tuple[name: string, price: int] のように書けるため、「どんな項目を持つデータなのか」をはっきり表現できる。
また、関数の戻り値として tuple を使えば、1つの関数から複数の値をまとめて返せる。
これは、計算結果と補足情報を同時に返したいときや、商品情報のようなまとまったデータを作りたいときに便利である。
注意点として、文字列と整数をつなげるときは $ で文字列に変換する必要がある。
項目名の書き間違い、文字列のクォーテーション忘れ、関数呼び出しの () 忘れも初心者が詰まりやすいので、コードを読むときに必ず確認するべきである。
次回は、配列とseqについて学ぶ予定である。
配列やseqは、同じ種類の値を複数まとめるための仕組みである。