見出し画像

第8章 プログラミング(関数編)

 プログラミングをする上で関数という考えは非常に大事です。あらゆる場面で必要になると思われます。現在のプログラムを作る上では切っても切り離せない関係と言えるでしょう。

1.プログラムの部品化
2.関数とは
3.関数の構成要素
4.関数の文法
5.変数を使える範囲
6.戻り値が指定されていない関数
7.アイテムが最大の個数かを確認しよう!

プログラムの部品化

 以下のソースコードはRPGでアイテムの個数が最大であるかを確認する処理です。

extends Node2D

func _ready():
	
	#アイテムの個数
	var item_num = 10
	
	print("このアイテムの個数は…")
	if item_num == 10 :
		print("最大です。")
	else:
		print("最大ではありません。")

 item_num に入る値によって「最大です。」と表示するか「最大ではありません。」と表示するかを決めています。

 それでは4つのアイテムの数が最大であるかを確認するプログラムはどうなりますか?

 素直な考えをすれば同じ処理を複数書けばよいのですが…。

extends Node2D

func _ready():
	
	#アイテムの個数
	var item_1_num = 10
	var item_2_num = 3
	var item_3_num = 10
	var item_4_num = 8
	
	print("アイテム1の個数は…")
	if item_1_num == 10 :
		print("最大です。")
	else:
		print("最大ではありません。")
	
	print("アイテム2の個数は…")
	if item_2_num == 10 :
		print("最大です。")
	else:
		print("最大ではありません。")
	
	print("アイテム3の個数は…")
	if item_3_num == 10 :
		print("最大です。")
	else:
		print("最大ではありません。")
	
	print("アイテム4の個数は…")
	if item_4_num == 10 :
		print("最大です。")
	else:
		print("最大ではありません。")

 これはあまりにも長すぎます。もしこれがアイテムの種類が4種類ではなく100種類あった場合を考えるとゾッとしますね。もし「最大です」の表示から「アイテムはいっぱいです。」の表示に変えようものならその作業は計り知れません。

 しかしよく見ると、同じような処理がいくつもあります。「最大かそうでないかを確認する処理」が非常に似ています。この部分を切り離して部品化できれば、もっと簡単なソースコードになりそうです。

 この部品化をするために必要なものが関数です。


関数とは

 プログラムで関数とは引数に依存して決まる値という意味です。あまり数学が得意でなくても簡単な式で理解できます。

 実際に式を見ながら見ていきましょう。

画像1

 ここでは f が関数x が引数です。ここでは f を f 関数と呼びます。
 xに1という値を渡すと f 関数は「1」という値になります。(x² = 1² = 1)
 xに2という値を渡すと f 関数は「4」という値になります。(x² = 2² = 4)

画像2

 ポイントとなるのは「 x の値が決めっている」ことです。関数とは引数に依存して決まる値なので「 f 関数は x に依存して決まる値」ということになります。x が決まらないと f 関数も決まりません。

  ではxに入る値が何も決まっていないときはどうなるでしょうか。

画像3

  f 関数は壊れてしまいました。x に依存しているのに x を指定されなかったら当然です。つまり関数は引数に依存しているため、引数を指定してあげないとエラーとなるということです。

 引数の指定は少なくても多くてもダメです。「f(x)」であれば「x」を指定しなくてはなりませんし、「g( x , y )」であれば「x」と「y」を指定しなくてはなりません。


関数の文法

 GDScriptにおいて関数の文法は以下の通りです。

定義の文法

func <関数名> ( <引数1> , <引数2> , ... , <引数n> ) :
   <関数の中の処理>
   return <戻り値>

関数の呼び出しの文法

<関数名> ( <引数1> , <引数2> , ... , <引数n> ) 

 今までと比べると少々複雑かもしれませんが、実は皆さんはすでに関数を定義しています。さらには呼び出しています。

 それは「func _ready()」と「print」です。第5章でほんの少し話題に上がりましたが、「func _ready()」の真の正体は関数です。皆さんは今まで_ready関数を定義して、print関数を呼び出していたことになります。
(本来_ready関数とは言わず_readyメソッドと表記しますがここではオブジェクト指向の話ではないため_ready関数と話させていただきます。)

 それでも1つだけ見慣れない言葉があります。「戻り値」です。もう一度、関数の定義を見てみましょう。

「関数とは引数に依存して決まる

 引数に依存してきまる値…つまり関数は結果的に値です。これを戻り値と言います。先ほど例にあげた f 関数も x が 2 であれば f 関数は「4」としっかり値になっていました。

 returnは関数の戻り値を指定することができます。

 サンプルコードと実行結果をご覧ください。

extends Node2D

func _ready():
	
	var value
	
	#my_function関数を呼び出す
	value = my_function( 10 )
	
	# 結果を表示
	print( value )


#aを二乗した値を返すmy_function関数を定義
func my_function(x) :
	return x * x

 実行結果

画像4

 これは x を二乗するmy_function関数を定義して、_ready関数でmy_function関数を呼び出しています。

画像5

1.my_functionの引数xに10を指定する。
2.my_functionは引数xに10を代入して処理を開始する。
3.my_functionが100という値に決まる。
4.my_function(10)が100となり、valueに100を代入される。

 関数を呼び出し場合は今までのプログラムと違い、「上から下へ」ではなく「関数の場所から始まる」ことに注意してください。

 今回の下側に関数の定義を書きましたが上側に書いても問題ありません。

extends Node2D

#aを二乗した値を返すmy_function関数を定義
func my_function(x) :
	return x * x

func _ready():
	
	var value
	
	#my_function関数を呼び出す
	value = my_function( 10 )
	
	# 結果を表示
	print( value )

 どちらが良い、というわけではありませんが筆者は関数の定義を下に書くほうが好きです。

 今回は引数が1つでしたが、引数が2つ、3つもしくは無くても問題ありません。

extends Node2D

func _ready():
	#自作の関数を呼び出し
	print_position_2( 10 , 20 )
	print_position_3( 10 , 20 , 30)
	print_hello()

#引数が2つの関数
func print_position_2( x , y ):
	print( "現在の座標 x:" , x , " y:" , y )
    return 0

#引数が3つの関数
func print_position_3( x , y , z ):
	print( "現在の座標 x:" , x , " y:" , y , " z:" , z )
    return 0

#引数がない関数
func print_hello():
	print("こんにちは!")
    return 0

 実行結果

画像6


変数を使える範囲

 以下のソースコードをご覧ください。

extends Node2D

func _ready():
	
	#変数宣言
	var value # 変数valueを宣言
	var value # 変数valueをもう一度宣言(エラー!!!)

 実行すると以下のような表示が出てエラーになります。

画像7

 なぜエラーになったかというとすでに宣言した変数をもう一度宣言しているためです。同じ変数名を持つ変数が2個以上あると「value = 10」という処理を行う上でコンピューターはどの変数に代入すればわからなくなるからです。

 では次のケースはどうでしょうか?

extends Node2D

func _ready():
	var value #valueを変数宣言した
	print("_readyでvalueを変数宣言しました。")
	
	#自作関数を呼び出す
	my_function()

func my_function():
	var value #valueを変数宣言した
	print("my_functionでvalueを変数宣言しました。")
    return 0

 これを実行すると正常に処理をしてくれます。

画像8

 正常に処理、つまりエラーは起きていません。これは変数を使える範囲が関わっています。

 変数には有効範囲があります。それはブロックの中です。第7章の if文を知る途中にあったブロックです。関数を定義する際にもブロックを作っています。

func <関数名> ( <引数1> , <引数2> , ... , <引数n> ) :
   <ブロックの処理1>
   <ブロックの処理2>
   <ブロックの処理3>
   …

 先ほどのソースコードでは_ready関数とmy_function関数のブロックそれぞれに変数宣言をしました。_ready関数のブロック内で変数宣言をしたvalueはそのブロック内でしか使えませんし、my_function関数のブロック内で変数宣言をしたvalueはブロック内でしか使えません。つまり別々の変数になるということです。

画像10

 _ready関数の中で変数宣言をした場合、別の場所でその変数を使うことはできません。ブロックが違うため全く別の変数になります。

extends Node2D

func _ready():
	
	#valueを変数宣言した
	var value
	
	#自作関数を呼び出す
	my_function()


func my_function():
	
	#readyの中で宣言した「value」に代入をしてみる(エラー)
	value = 10

    return 0

 実行すると以下のエラーが現れます。

画像9

 「value」変数がないよというエラーです。

 変数宣言はブロック外でも可能です。

extends Node2D

var count = 0  # <-------- ブロック外の変数宣言

func _ready():
	
	#自作関数を呼び出す
	counter()
	counter()
	counter()
	counter()
	
	print("最終的なcount:" , count )


func counter():
	count = count + 1
	print( "現在のcount:" , count )

 この場合は、このソースコード内すべてで使える変数となります。


戻り値を指定されていない関数

 関数は必ず戻り値を指定されているわけではありません。以下のソースコードを見てみましょう。

extends Node2D

func _ready():
	
	#レベル
	var level
	
	level = 12
	
	# 現在のレベルを表示
	print_level( level )

#レベルを表示する関数
func print_level( level ) :
	print( "現在のレベルは" , level , "です。")

 print_level関数には今まで最後にあった「return」がありません。これは戻り値が指定されていない関数です。

 その出力する値を確認してみましょう。

extends Node2D

func _ready():
	
	#レベル
	var level
	var result
	
	level = 12
	
	# 現在のレベルを表示
	result = print_level( level )
	
	#戻り値がない関数の値
	print( result )

#レベルを表示する関数
func print_level( level ) :
	print( "現在のレベルは" , level , "です。")

 実行結果

画像11

 戻り値が指定されていないと空。つまりprint_level関数はNullになります。第5章にあった通りNullは何もないを表します。


アイテムが最大の個数かを確認しよう!

 ここからは少し練習をします。まず最初に出てきた4つのアイテムの数が最大であるかを確認するプログラムを見ましょう。

extends Node2D

func _ready():
	
	#アイテムの個数
	var item_1_num = 10
	var item_2_num = 3
	var item_3_num = 10
	var item_4_num = 8
	
	print("アイテム1の個数は…")
	if item_1_num == 10 :
		print("最大です。")
	else:
		print("最大ではありません。")
	
	print("アイテム2の個数は…")
	if item_2_num == 10 :
		print("最大です。")
	else:
		print("最大ではありません。")
	
	print("アイテム3の個数は…")
	if item_3_num == 10 :
		print("最大です。")
	else:
		print("最大ではありません。")
	
	print("アイテム4の個数は…")
	if item_4_num == 10 :
		print("最大です。")
	else:
		print("最大ではありません。")

 このソースコードにはいくつもの同じような処理をしている場面がありました。それは最大かそうでないかを確認するという処理でしたね。

 この部分を関数にしてみます。まずは定義だけ載せます。

func check_item_num( item_num ):
	if item_num == 10 :
		print("最大です。")
	else:
		print("最大ではありません。")

 このcheck_item_num関数はitem_numの引数に値が入ると最大かどうかを確認してくれる関数となっています。

 実際にこの関数を定義して、呼び出したソースコードが以下になります。

extends Node2D

func _ready():
	
	#アイテムの個数
	var item_1_num = 10
	var item_2_num = 3
	var item_3_num = 10
	var item_4_num = 8
	
	print("アイテム1の個数は…")
	check_item_num(item_1_num)
	
	print("アイテム2の個数は…")
	check_item_num(item_2_num)
	
	print("アイテム3の個数は…")
	check_item_num(item_3_num)
	
	print("アイテム4の個数は…")
	check_item_num(item_4_num)


func check_item_num( item_num ):
	if item_num == 10 :
		print("最大です。")
	else:
		print("最大ではありません。")

 関数で1つの処理にまとめたことによって_ready関数の中身は非常に簡単なものになりました。

 もちろん簡単になるだけではありません。「最大です。」の表示から「アイテムはいっぱいです。」に変えることになってもcheck_item_num関数の中身を変えればすぐ終わります。

func check_item_num( item_num ):
	if item_num == 10 :
		print("アイテムはいっぱいです。") # <----- ここを変えるだけ
	else:
		print("最大ではありません。")

 もし関数を知らずに、このまま if文のみで実装し続けていたら1つの変更をするのに100ヵ所も200ヵ所も変更する作業を延々とやり続けることになっていたでしょう。


 関数とは非常に奥深くありながら考え方は単純です。
 ・引数を与えれば関数は動く。
 ・同じ処理があれば関数にできないかを考える。
 この2点を意識してプログラミングをしていきましょう。



この記事が気に入ったらサポートをしてみませんか?