百億のマインクラフトと千億のタートル

つまり、マイクラワールド1つあたりタートルが10匹生息しているわけですね。

手続きに名前をつけてやると自作関数が作りやすいのです

はじめに

hevo2.hatenablog.com

前回からだいぶ間があきましたが、床ブロック敷設プログラムは役立っていますか?

コマンド入力時にコマンドライン引数を与えることで長さを指定できるの便利ですよね。

今回はこのプログラムの内容を再検討してみましょう。

床ブロック敷設プログラム

前回のプログラムの再掲。

なかなか長いプログラムですが、やっていること自体は意外とシンプルなのはこれまでのブログ記事を読んでいるとわかりますよね。

でもこんなことを思いませんか?

「なんだか、同じようなことを何度も繰り返して書いてない?」

以前、繰り返しfor文を紹介したとき(過去記事)に、「同じ手続きを何度も書くのは面倒なので、代わりにfor文使って回数指定して繰り返す」方法について説明しました。for文を使うことで冗長で無駄な記述が減って、全体がすっきりしました。

今回は、また別の、すっきり書く方法を説明します。

冗長な部分で何をやっているのか一言で説明しよう

まず特徴的なのは、以下のような手続きがコード中に二回出現していることです。

-- アイテムが入っているスロットを選択する
for slot=1,16 do
  local kosuu = turtle.getItemCount( slot )
  if (kosuu > 0) then
    turtle.select( slot )
    break
  end
end

行番号で言うと、19〜26行目と42〜49行目の二箇所です。同じ内容を2回書いているのでなんだか無駄な気がしますね。

ではこの手続きは何をやっているのでしょうか?

一言で要約するならば、「アイテムが入っているスロットを選択する」手続きですよね。

この手続きに注目してみましょう。

手続きに名前をつけてやる

この「アイテムが入っているスロットを選択する」手続きですが、コード全体では「アイテムが入っているスロットを選択する」手続きが二箇所あるわけで、なんとかこの「アイテムが入っているスロットを選択する」手続きを省略して書く方法を・・・。

・・・長いよ!一言じゃないよ! この手続きにはもっと短い名前をつけなおしましょう。

たとえばちょっとだけかっこつけて英語で名前をつけましょうか。

「selectItemSlot」(≒ select (an) item slot ≒ アイテムスロットを選択せよ)

とかどうでしょう。

新しく関数を定義する

プログラム言語では、何度も使う決まり切った手続きを、関数(サブルーチンと呼ぶこともある)として定義できます。一度関数として定義してしまえば、その関数は使い放題となります。

自分で新しく関数を定義するには、以下の表記方法を使います。

function 関数名()
  やって欲しい手続きを書く
end

この表記方法を使って、以下のように「selectItemSlot」関数を定義しましょう。

function selectItemSlot()
-- アイテムが入っているスロットを選択する
for slot=1,16 do
  local kosuu = turtle.getItemCount( slot )
  if (kosuu > 0) then
    turtle.select( slot )
    break
  end
end
end

今回は上記のように、「function 関数名()」〜「end」の間に、「selectItemSlot」の具体的な手続きをコピペしました。

ただこのままだと、インデント(行頭の空白スペース)が不揃いで不格好なので、以下のように整えました。

function selectItemSlot()
  -- アイテムが入っているスロットを選択する
  for slot=1,16 do
    local kosuu = turtle.getItemCount( slot )
    if (kosuu > 0) then
      turtle.select( slot )
      break
    end
  end
end

以下のようにシンプルに

このように新しい関数「selectItemSlot」を定義できたわけですので、この新関数を使って冗長な手続きを置き換えるとこんな感じになります。

-- 床にブロックを敷き詰めながら指定した歩数だけ前に進む
for i=1,hosuu do
    -- アイテムが入っているスロットを選択する
  for slot=1,16 do
    local kosuu = turtle.getItemCount( slot )
    if (kosuu > 0) then
      turtle.select( slot )
      break
    end
  end
 
  turtle.placeDown()
  turtle.forward()
end

上記の冗長な表記から

以下のような、関数を使ったシンプルな表記へ

-- 床にブロックを敷き詰めながら指定した歩数だけ前に進む
for i=1,hosuu do
  selectItemSlot()
 
  turtle.placeDown()
  turtle.forward()
end

ここでのポイントは、作った関数「selectItemCount」を実行するためには、末尾に「()」をつけて、selectItemCount()のように書くことです。「()」を忘れると実行できないので要注意。

自作関数を利用した完成版プログラム

最終的に以下のようなプログラムになります。

このプログラムには3つの重要なポイントがあります。

まずコメントの------- Config ------のような区切り線に注目しましょう。

注目することで、プログラム全体の構造が以下のようになっていることがわかります。

  • Config 部: プログラム全体の設定パラメーターを記述する部分。挙動を変えるために書き換えても良い。
  • Functions 部: 全体をすっきりと表記するために、新しく関数を定義する部分。
  • Main 部: 実際にコンピュータが実行する手続きを表記する部分。

このように3部構成になっていることに気づくことが最初のポイントです。

2つ目のポイントとして、 初めて見るプログラムを読み解く時には、このMain部分から始めることが重要です。

Main部分を読んでいくうちに、謎の(知らない)関数が出てきたら、それはプログラム作成者が作った新しい関数である可能性があります。Function部に戻ってその関数を探し、何をする関数なのか解析しましょう。

なお、自作関数の名前を「selectItemSlot」のようにわかりやすいものにしておけば、「アイテムのスロットを選んでくれる関数」だとある程度推測がつきますので、Function部を読まなくても全体の動きが掴めるようになります。

関数の名前は、その挙動を説明するわかりやすいものにすることを心がけましょう。

つまり、「関数の名前つけは重要!」

3つ目のポイントとして、関数の定義は実際にその関数を実行するよりも前で行いましょう。つまり、関数を定義するFunction部は、実際にそれを実行するMain部よりも前に置くことがポイントです。

読者への宿題

このプログラムは、他にも冗長な記述がたくさんあります。

たとえば、以下のような関数をFunction部で定義して、その関数を使ってシンプルに書き換えてみましょう。

  • spreadBlockAndForward() : 真下にブロックを設置して前に1歩進む関数
  • goRight() : 右に移動する(右を向いて前に1歩進む)関数

おまけ

スピッツのこの曲。いいよね。 (アフィ広告ではなくAmazon商品へのただのリンクです)

名前をつけてやる

名前をつけてやる