読者です 読者をやめる 読者になる 読者になる

小学校の先生のためのSwift Playgrounds講座 コードを学ぼう1<関数>【解答】

f:id:PSYuki:20170403225928p:plain

こんばんは。ツムさんです。

 

プログラミングなんてやったことないぜキリッ

 

という先生のための Swift Playgrounds を使ったセミナーです。

 前回は、Byteくんたちを動かすための「コマンド」を学びました。

 

psyuki.hatenablog.com

 

今回は、「関数」です。

 

 

関数って何?

関数と聞くと、

 

y = x + 3 

 

みたいな物をイメージしますが、数学の関数とは意味が異なっています。あえて数学チックに書くなら f(x) が近い表現ですね。xの値を受け取って計算された値を返す。というイメージ。これを前回ならった「コマンド」に拡張した概念が「関数」です。

 

一応、教科書的には、

関数とは、引数と呼ばれるデータを受け取り、定められた通りの処理を実行して結果を返す一連の命令群。多くのプログラミング言語では、関数がプログラムを構成する要素となっている。多くの言語や処理系では、開発者の負担を軽減するため、よく使う機能が関数としてあらかじめ用意されている。

IT用語辞典より

関数とは|ファンクション|function : 意味/定義 - IT用語辞典

 

 ということなんですが、実際に「関数」に触れてみたほうが理解しやすいので、早速Swift Playgroundsで作ってみましょう。

 

Swift Playgroundsの関数とは

Swift Playgroundsにおける「関数」は、Byteくんを動かすためのコマンドを集めたものになります。たとえば、Byteくんを3歩進めるための関数は、こうなります。

 

func threeMove() {

 moveForward()

 moveForward()

 moveForward()

}

 

funcが、関数を定義するよ。という宣言です。threeMoveは関数名で、名前は自由に付けられます。

 

関数を作ると、9歩進んで宝石を取るステージがあったとすると、threeMove関数がないと、

 

 moveForward()

 moveForward()

 moveForward()

 moveForward()

 moveForward()

 moveForward()

 moveForward()

 moveForward()

 moveForward()

 

 collectGem()

 

という感じの長ーいプログラムになってしまいますが、threeMove関数が用意されていると、

 

threeMove()

threeMove()

threeMove()

collectGem()

 

と書けるようになるわけです。3つのmoveForwardコマンドの部分を1つの関数に置き換えています。

 

新しい挙動を作る

このステージは関数は登場しません。その前準備です。

f:id:PSYuki:20170408224442p:plain

既存のコマンドを使って右を向くようにプログラムを作ります。前回までで登場したコマンドは、

  • collectGem() 宝石を取る
  • moveForward() 1歩進む
  • toggleSwitch() スイッチを押す
  • turnLeft() 左を向く

の4種類でした。

右を向くためには、どのコマンドをどう使えばいいのかというと、、

f:id:PSYuki:20170408224612p:plain

簡単ですよね。左を向く(turnLeftコマンド)を3回繰り返すと、最初の方向から見ると右を向きます。

 

あとは、歩数を数えて、moveForwardとcollectGemを並べてあげれば出来上がりです!

 

新しい関数を作る

いよいよ関数を作ります。と言っても下ごしらえは既に終わっているので、このステージは楽です。

 

ステージには、3つのスイッチが配置されていますが、最終地点以外のスイッチは点灯しているので押す必要がありません。ワープマスも使いながら、Byteくんを右左と動かしていき、最後にスイッチを押したらクリアです。

f:id:PSYuki:20170408224934p:plain

右を向く関数のひな型が用意されているので、関数の中身を書くだけで関数が完成します。右を向くためには左を向くコマンドを3回実行すればよいのでした。

ということは、

f:id:PSYuki:20170408225320p:plain

turnLeft()コマンドを3つ並べてあげれば出来上がりです。

 

あとは、歩数を数えながら向きを変えながら進めていけば、、

f:id:PSYuki:20170408225431p:plain

クリアです!

func文で作成した関数は、自動的にコマンド欄に登場するので、作った関数もタップするだけで使うことができますよ。

 

集めて、切り替えて、繰り返す

このステージも関数を作ったほうが楽になります。ステージを見ると分かりますが、各辺の処理には必ず下の5個のコマンドが必要です。

  1. 1歩進む
  2. 宝石を取る
  3. 1歩進む
  4. スイッチを押す
  5. 1歩進む

気をつけたいのは、スイッチの先のマスが1マスの辺と2マスの辺があるところです。

6番目のコマンドに、左を向く辺と、1歩進んでから左を向く辺があるということですね。

f:id:PSYuki:20170408231520p:plain

まずは、1~5のコマンドを並べて関数を作ります。関数名は、ここではgetGemAndToggleSwitchにしました。宝石をとってスイッチを押す関数という意味です。

f:id:PSYuki:20170408231951p:plain

あとは、getGemAndToggleSwitchと左を向くturnLeftコマンドを組み合わせればプログラムの完成です!

関数の後、1歩進んでから左を向くときと、直ぐに左を向く辺があることに注意しましょう。

 

往復する

 このステージは3x3のマスに9個の宝石が並んでいます。一列の宝石を取る処理を関数で定義できれば、その関数を3回呼び出すことでプログラムを短く書けそうです。関数にすべき処理のパターンを見つけるのがポイントです。

f:id:PSYuki:20170408234012p:plain

スタート地点にいるByteくんの頭上にも1個宝石が浮かんでいる点に注意しましょう。宝石を回収しながら進み、突き当りまで進んだら後ろ向きに方向を変えて、戻ってきます。これを往復(ouhuku)関数として作ります。後ろに向きを変えるためには、turnLeftもしくはturnRightコマンドを2回実行すれば後ろ向きになりますね。

 

宝石を1列取り終わったら隣の列へ進まなければならないので、ouhuku関数を使った後に、その処理(ouhuku関数で挟まれた部分)を加えます。

f:id:PSYuki:20170408234038p:plain

クリアできました!

 

パターンをネストする

ネストというのは、関数の中から別の関数を呼び出すことを指します。このステージでは、最初に後ろを向く関数(turnAround())を作り、その関数を階段を呼び出す関数から呼び出します。

 

後ろを向くためには、2回左を向けば丁度180°後ろを向くことになるので、ここではturnLeftコマンド2つ並べます。

f:id:PSYuki:20170408234357p:plain

次に階段をクリア関数は、宝石をとってUターンして戻ってくる処理をまとめます。こうすることで solveStair関数を4回使った短いプログラムを作ることができます。

Uターンして戻ってきたら、隣の階段に向きを変えなければいけません。2つのsolveStair関数の間にturnLeft関数を挟んであげれば、向きを90°変えながら4つの階段を上って宝石を取るプログラムが完成します。

f:id:PSYuki:20170408235000p:plain

クリアできました!

 

並んだ階段

前のステージ同様に、関数をネストすることで、1つの処理を複数の処理(関数)に分解します。

このステージは、前後に宝石が配置された列が3列並んでいます。1つの列を処理する関数を作ればいいのはすぐにイメージできると思います。

1つの列をクリアするためには、前後に置かれた宝石をとって戻ってくる(往復)することが必要です。

3列 ⇒ 1列 ⇒ 往復

と分解します。

関数の定義は、細かい処理を先(上)に書く必要があります。つまり往復の処理を最初に作ります。

f:id:PSYuki:20170409002528p:plain

往復するためには、2マス進んで宝石を取って後ろを向いて2マス進む ことで実現できます。これをcollectGemTurnAround関数として作ります(上)。

 

そして、この往復関数を使って1列をクリアする関数(solveRow)を作ります(下)往復するとスタートの向きと丁度反対を向いて戻ってくるので、そのままもう一度collectGemTurnAround関数を使えばいいことが分かります。

前後の宝石を取ってスタート位置まで戻ってきたら、隣の列へ進みます。

f:id:PSYuki:20170409002854p:plain

solveRow関数はこんな感じになります(上)

 

最後にsolveRow関数を3回呼び出せば、、

f:id:PSYuki:20170409003159p:plain

クリアです!

 

パターンを探す

関数のラストステージも、関数をネストします。効率よくネストするためにも、繰り返し処理できるパターンは、なるべく細かい粒度(少ないコマンド)で関数を作るのがポイントです。関数の名前は、それとわかる機能を1つ2つ程度に絞ってつけます。たとえば、後ろを向く(turnAround)や前に進んで宝石をとる(moveThenCollectGem)といった具合です。

 

このステージは、2マスさきにスイッチが置かれている箇所が全部で6個あります。処理の分解の流れは、

4列処理 ⇒ 1列処理 ⇒ スイッチ1個の処理

に分解できます。

スイッチ1個の処理は、往復する列(短辺)と、往復せずにそのまま先へ進む列(長辺)があるので、復路の処理を関数に含めてしまうと都合が悪いです。

 

ここでは、2マス進んでスイッチを押す処理を最小粒度の関数(moveThenToggle)として定義します。復路の関数は、後ろを向いて2マス進むもしくは後ろを向いて4マス進むの2種類があるので、それぞれ関数にします(turnAroundThen2ForwardとturnAroundThen4Forward)。

f:id:PSYuki:20170409004555p:plain

turnAroundThen4Forward関数は、turnAroundThen2Forward関数をネストして使っている点に注意してください。turnAroundThen2Forward関数の後ろに2つのmoveForwardコマンドを並べるとturnAroundThen4Forward関数を作ることができます。

 

最後に、短辺に対しては、moveThenToggleとturnAroundThen2Forwardを使い、長辺については、moveThenToggleとturnAroundThen4Forwardを使ってステージをクリアします。

f:id:PSYuki:20170409005357p:plain

90°ずつ向きを変えて時計回りにスイッチを押すような処理でもクリアすることはできますが、上のプログラムのように短辺を先にクリアしてから長辺に移るようにすると、向きを変える(turnLeftコマンド)のは1回で済み、プログラムを短く書くことが出来ます。

 

どうでしたか?

関数を使うと、同じ処理を繰り返すような場面で、プログラムを短く読みやすく書くことができました。次はforループを学びます。