(プログラミング 小手調べ解説編 第1章)
1 から 20までの数を(縦に)印字させる。
(1..20).each do |i|puts i end
# または
for i in 1..20 do puts i end
上記の下の例のような for を使った構文では、in の次に並べたものは、データの供給元(forを使って1つずつ取り出すための)として機能するもの(右図)。
ここでは Range オブジェクトが置かれている(後述)が、この構文(上の eachを使った方でも、for を使った方でも)では、供給元になれるものなら何でも使える。
for i in range(1,20):
print(i)
# 一行につないでもいい
for i in range(1,20): print(i)
いくつかの書き方があるのでまずはプログラム例を示す。
// ①昔ながらのループ
for(let i=1 ; i<=20 ; i++) console.log(i)
// でも let (新しめの変数宣言キーワード)は使っておく
// ②イテレータを使う(JSの場合ちょっと面倒だが)
for(let i of Array(20).keys()) console.log(i)
// ③配列にしてから forEach メソッド(無名関数を渡す)
[...Array(20).keys()].forEach(i=>console.log(i+1))
// スプレッド構文についてもついでに知っておこう(あとで)
JSでは、古くからの(CやJavaと同様の)括弧内に3つの式を ’;’区切りで置く記法も 使われている(①の書き方)。
それ以外に、(先にRuby,Python で使ったのと同様の、データ供給元を(JSでは、in ではなく of の後ろに)指定した forの構文もある(②の例)。
ここ使われるデータ元、Array(20).keys()
は、
サイズ20の配列(各項の値としては何も入っていない = undefined が値)を作っておいて、
その各項の添え字を並べた列(を生成するもの)を、メソッド keys()
に作らせたもの。
この式の値は、(実体のある)配列ではなく、イテレータと呼ばれる(他の言語でもある)、データの並びを提供するオブジェクト。いわば抽象的なデータ。
これを具体的なデータ(たとえば配列)にするために、スプラット演算子とかスプレッド構文などと呼ばれる記法(配列を囲む左ブラケットの直後に連続するピリオド3つ)で、
配列の中にイテレータが供給するデータを強制的に展開させる。
もう1つ、繰り返しを実現する記法として、配列を出発点として高階関数 forEach メソッドを呼ぶという書き方がある(例③)。
有限の(両端の値を指定して)整数列を生成させ、
その各要素に対して、関数 print を適用させる
mapM print [1..20]
..
による記法は Rubyなどとも共通
高階関数 mapM は、以下のようにやや複雑
Prelude> :t mapM
mapM :: (Monad m, Traversable t) => (a -> m b) -> t a -> m (t b)
だが、使い方としては単純(解説記事を参照)
補足:
Haskell では、多くの関数が「純粋」な関数で、入出力や状態変化を発生させないものとして扱われるが、
入出力など一部の用途で純粋でない関数も使われる。その多くが「モナド」(Monad)と呼ばれる種類のもので(詳細説明は省くが)関数 print もモナドの一種(下の :t でタイプ(データの型)を表示させると IO という単語が含まれていることでわかる)。
高階関数として map があり(後述)、データ列からデータ列を生成させるのに使われる。
初学者は この map の方から学ぶほうがわかりやすいだろう。
mapM は、データ列に対して、モナド(に属する関数)を適用させるためのもの。
Prelude> :t map
map :: (a -> b) -> [a] -> [b]
Prelude> :t print
print :: Show a => a -> IO ()
Prelude> map ((+) 1) [1..20]
[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]