初級で学ぶこと: 名前のある関数を呼び出す
function abc() {
console.log("hello")
}
SetTimeout(abc,5000) // 5秒後に発火させる
// 訂正 最初、SetInterval(abc,5000) と書きましたが誤り。
// setInterval は5秒おきに繰返し発火するので
// (これらの遅延関数は目的に応じて適宜使い分けて下さい)
SetTimeout('console.log("hello")',5000)
のようにコード(を表す文字列)を直接渡すこともできる (が、安全性や安定性の観点から、初心者以外に好んで使う人はいない)。中級以降の常識: 手続きをコードの形で(文字列ではなく無名関数として)渡す
SetTimeout(function(){
console.log("hello")
},5000)
// または
SetTimeout(()=>{
console.log("hello")
},5000)
// 或いは、無名関数の中の式が1つならば
SetTimeout(()=>console.log("hello"),5000) // ブレース省略
1 から 10 までの整数を(この順に)出力させる:
for(let i=1 ; i<=10 ; i++) console.log(i)
範囲を指定して繰り返させる(いくつかの言語での実例)
for i in range(1,11): print(i) # pythonでは範囲の上限値が範囲に含まれない
for i in 1..10 do puts i end # do ~ end の代わりに { ~ } で囲んでもいい
{ ~ }
(do ~ end でも同様)で囲んだものを Rubyでは「ブロック」と呼び、無名関数に似た使い方をしている。for i in [1..10]
console.log i
mapM print [1..10]
範囲、またはそれを実体化した(データが実際に並んだ)配列やリスト、を繰り返しのガイドレールのように扱い、
map や each系のメソッド(または関数)を使って繰り返させる(これもいくつかの言語で紹介する)
あわせて、2乗の数列を得る書き方(map または内包表記)も併記しておく。
(1..10).each{|i|puts i}
(1..10).map{|i|i**2} # 2乗の数列
# rubyには内包表記はない
# python では each系のメソッドはなく、繰り返し動作は for を使う
list(map(lambda i:i**2, range(1,11)))
[i**2 for i in range(1,11)]
python の糖衣言語 coconut の例も
range(1,11)|>map$(i->i**2)|>list
# 内包表記はpythonと同じ
pythonでは範囲の第2引数は範囲に含まれない(ので11を指定した)。
map (^2) [1..10]
[i^2|i<-[1..10]]
たとえば paiza で実行するなら、
main=print ( map (^2) [1..10] )
-- または
main=print $ map (^2) [1..10]
のように呼び出す。
javascriptの場合は、範囲を表すオブジェクトがないので、 配列を生成する独自の記法を出発点にすることになる。
[...Array(10).keys()] // => 0 ~ 9の配列
// 1 ~ 10 にするには以下のような方法がある
[...Array(10).keys()].map(i=>i+1)
[...Array(11).keys()].slice(1)
//
[...Array(11).keys()].slice(1).map(i=>i**2)
階乗
function fact(n) {
return n>1 ? n*fact(n-1) : 1
}
[...Array(n+1).keys()].slice(1).reduce((a,b)=>a*b)
前の定義では、
fact(n-1)
)に対して、n * その値
の乗算)を行ってからそのため、子フレームが終了するまでその親フレームは処理の途中で待機しておく必要がある。
子フレームの値を(その後の演算をせずに)そのまま自フレームの値として返すようにプログラムすると、
前節のコードを末尾再帰に直すと以下のようになる。
function fact_t(n,r=1) { // 返すべき値の種 r の初期値は第2引数の省略値として渡す
return n>1 ? fact_t(n-1,r*n) : r
}
// または
function fact_t2(n) { // 内部関数として末尾再帰関数を用意する
function fact_t2_i(n,r) {
return n>1 ? fact_t2_i(n-1,r*n) : r
}
return fact_t2_i(n,1)
}
'100101001'
などの文字列(以下の説明では変数 s とする)が与えられたときに、 これを(2進数だと解釈して) Integer型の数値に変換することを考える。
このときは末尾再帰で文字列の先頭から見ていくと書きやすい
function b2d(s,r=0){
return s.length>0 ? b2d(s.slice(1),r*2+(s[0]-0)) : r
}
末尾再帰でない書き方だと文字列の末尾から見ていく書き方になる(コードは略)。
これは配列を使っても書ける
s.split('').reduce((r,c)=>r*2+(c-0),0)
十進二進変換
function d2b(n) {
return n>0? d2b(~~(n/2))+'01'[n%2] : ""
}
// または末尾再帰で
function d2b_t(n,r="") {
return n>0? d2b_t(~~(n/2),'01'[n%2]+r) : r
}
フィボナッチ数
function fib(n){
return n<2? 1 : fib(n-1)+fib(n-2)
}
たとえばこれを、 [...Array(n).keys()].map(fib)
のように呼んでみよう。 (n に適当な数を入れる。ただしnが大きいと戻ってこなくなるのでご注意)。
マージソート
大枠
function mSort(ary) {
// 再帰を前提にしましょう 詳細はこれから。
} // 与えられた配列を降順に並び替えた配列を返すことにしておく
ストーリー:
末端条件:
ary の長さが1以下になれば、これ以上分割する必要はないし、 ソートもされているとみなすことができる。
ので、ary をそのまま返せばいい。
末尾再帰について:
マージ: こんな枠組みになる
function mMrg(a1,a2) {
// これも再帰を前提にする(末尾再帰にできる)
} // 1本にまとまった(ソート済の)配列を返すことになる
ということを考えてプログラムを作ってみましょう。 (サンプルへのリンクを前回資料末尾に置きました。)