基本的な関数の作り方(Haskell)

2022/04/27に公開

この記事では、関数の基本的な作り方について学びます。

Haskellには関数にまつわる概念がたくさんあります。再帰関数や高階関数など難しいトピックは除いて、ここでは「基本の関数」の作り方を紹介します。

関数とは何か?

データを投入すると、何らかの処理をして、結果を返却する仕組みです。 部品として処理を1まとめにすることで、より見やすく効率的にコードが書けます。

関数で重要な概念が3つあります。引数、戻り値、仮引数です。

  • 引数:外部から投入する値のこと
    → 関数に対して色々なデータを処理させられる
  • 戻り値:処理の結果として返却される値のこと
    → 計算結果の値をまた別の場所で再利用できる
  • 仮引数:それが定義された関数の中だけで使う一時的な変数のこと
    → 引数として外部から投入する値を受け取るために使われる

Haskellにおける「関数」の特徴

その中でもHaskellの関数は大きく3つの特徴を持っています。

1、全ての式が値をもつ
2、全ての関数が引数・戻り値を持つ
3、どんな時でも常に同じ値を返す

まるでサッカーのパス回しのように、関数同士が戻り値を返却し合います。関数同士による戻り値のやり取りによって、プログラムを組み立てるのがHaskellのスタイルです。

また式の値は増減したり途中で変わることはありません。プログラムの堅牢性を保つため、どんな時でも常に同じ値を返すことを期待されます。これを難しい言葉で参照透過性を持つ..と呼びます。

関数の定義

関数の定義はこちらです。

関数名 :: 引数 -> 戻り値
関数名 引数
    = 処理内容

命令型言語の関数と違って、すごくサッパリと記述していますよね。{ }などもないので初見の方は混乱しがちですが、結局していることは一緒です。

関数の作り方

受け取った数値を2倍にするnibai関数を例に作り方を学んでいきます。1番下の行にあるmain = print (nibai 10)では、nibai関数へ10という数値を渡しています。結果としては20という数値が返却されます。

nibai :: Int -> Int   --1行目
nibai num                        --2行目
    = num * 2                --3行目
    
main = print (nibai 10)

ここで注目して頂きたいのは上から3行の部分です。この部分で関数を作成しており、3つの内容を決めています。

1行目:引数、戻り値のデータ型はどうするか?
2行目:関数名、仮引数名はどうするか?
3行目:処理内容はどうするか?

1、引数、戻り値のデータ型はどうするか?

1行目では引数、戻り値のデータ型を指定しています。関数名の後に::(型宣言)を書きます。その後に矢印->で区切って、データ型を指定します。

nibai :: Int -> Int   --1行目
nibai num                        --2行目
    = num * 2                --3行目

引数と戻り値が区別されないのでややこしいですよね。これは常に引数を1つだけ受け取るためです。左の引数を1つ受け取る→右のモノ(関数or値)を返却..を繰り返します。(関数を戻り値として返せるのもHaskellの大きな特徴でしたね!)。同時に受け取る引数を1つにすることで、仕組みをシンプルに保つことができます。

ややこしい話をしましたが、ここではもっとカンタンに考えます。戻り値は1つしかないので、1番最後に書かれたデータ型が戻り値です。それより手前が引数のデータ型となります。 今回の引数は1つなので、Intは戻り値用・引数用で2つ書けばOKです。

引数・戻り値のデータ型はHaskellのプログラムが自動的に補足してくれます。分かりきったモノは書かなくてもOKですし、コンパクトに関数を記述できるのですごく便利です。

2、関数名、仮引数名はどうするか?

2行目では関数名、仮引数名を決めています。関数は必ず引数を持つので仮引数名を決める必要があります。

nibai :: Int -> Int   --1行目
nibai num                        --2行目
    = num * 2                --3行目

ここでは関数名をnibai、仮引数名をnumとしました。1番最初は絶対に関数名となるので、それ以後が仮引数となります。今回の引数は1つなので、それを受け取る仮引数も1つです。

3、処理内容はどうするか?

3行目では(=の後に続けて)処理内容を決めています。改行しなくてもOKですが、改行した場合はインデントを入れるのを忘れないで下さい。今回は単純にnumの数値を2倍にしています。

nibai :: Int -> Int   --1行目
nibai num                        --2行目
    = num * 2                --3行目

戻り値はどうなるのでしょうか?命令型言語の場合はreturnなどで明確に戻り値を定義していましたよね。

Haskellの場合、=の後に式(値を返却するモノ)を書きます。式に属するモノは、変数、関数、真偽値、計算式..などがあります。なので=の後の処理内容としては、(今回のように計算式であっても)値を返却するモノであれば何でもOKです。

関数呼び出し

最後の行で関数呼び出しを行なっています。関数を呼び出すには定義するときと同じです。関数名と引数を空白で並べて書きます。ここでは10という数値を引数として渡しています。

nibai :: Int -> Int   
nibai num                        
    = num * 2                
    
main = print (nibai 10)  --関数呼び出し

nibai 10によって関数を呼び出し、それをprint関数でそのまま出力にかけている..というわけです。

※)複数の引数の渡し方についてはこちらの記事をどうぞ

https://zenn.dev/masahiro_toba/articles/cea68bbe2cc556

Discussion