はじめに
「Hello World」はプログラムの実行環境における文字列出力のための題材です。
大抵がファーストステップとしての位置付けですが、プログラムがどう動くのかの題材として徹底解説すると30K文字を超える文章が必要となることもあります。
「Hello world」に続く題材としては、「Fizz Buzz」や「AtCoder 過去問精選 10 問」等が定番です。
これらはアルゴリズムを各言語でいかに実装するかが主体となるため、「Hello Wold」の次にプログラムがどう動くのかを考える題材は少ないと感じています。
そこで、同期/非同期処理の理解を深めるための題材として、**「Good Morning World」と「Good Night World」**をRubyとJavaScriptで書いてみました。
【定義】 Good Morning WorldとGood Night Worldとは
Good Morning World
1. 「アラームセット(N秒後に"Ring!Ring!Ring!"と表示する)処理」に続けて、「"Good Morning World"と表示する処理」が記載されていること。
2. 「アラームセット(N秒後に"Ring!Ring!Ring!"と表示する)処理」に続けて、「"Good Morning World"と表示する処理」が実行されること。
3. "Ring!Ring!Ring!"と表示された後に、"Good Morning World"と表示されること
実行結果例
Ring!Ring!Ring!
Good Morning World
Good Night World
1. 「アラームセット(N秒後に"Ring!Ring!Ring!"と表示する)処理」に続けて、「"Good Night World"と表示する処理」が記載されていること。
2. 「アラームセット(N秒後に"Ring!Ring!Ring!"と表示する)処理」に続けて、「"Good Night World"と表示する処理」が実行されること。
3. "Good Night World"と表示された後に、"Ring!Ring!Ring!"と表示されること
実行結果例
Good Night World
Ring!Ring!Ring!
解答例
以下のコードは、paiza.ioにて動作を確認しています。
Rubyでの解答例
def set_alarm n; sleep n; puts 'Ring!Ring!Ring!' end
set_alarm 0.1
puts 'Good Morning World'
def set_alarm n; Thread.new{ sleep n; puts 'Ring!Ring!Ring!' } end
t = set_alarm 0.1
puts 'Good Night World'
t.value # オンライン実行環境等で待受けるため
JavaScriptでの解答例
const set_alarm = async n => new Promise( resolve => setTimeout( () => { console.log('Ring!Ring!Ring!'); resolve() }, n));
(async () => {
await set_alarm(0.1);
console.log('Good Morning World');
})();
const set_alarm = n => setTimeout( () => console.log('Ring!Ring!Ring!'), n);
set_alarm(0.1);
console.log('Good Night World');
(補足) 他の回答について
callbackを用いたGoodMorningWorld
以下のJavaScriptのコードは、出力結果はGoodMorningWorldと同じになりますが、以下の理由でGoodMorningWorldではないと判定できます。
(理由) 「アラームセット処理」と「"Good Morning World"と表示する処理」の処理が、記載も実行も**「続けて」ではなく、「中に入って(ネストして)」いる**ため。
const set_alarm = (f, n) => setTimeout( () => { console.log('Ring!Ring!Ring!'); f() }, n);
set_alarm(
() => console.log('Good Morning World'),
0.1
);
実行順を変えたGoodNightWorld
以下のrubyのコードは、記載順も正しく、出力結果はGoodNightWorld同じになりますが、実行順を変えているため、GoodNightWorldではないと判定できます。
def reverse_call *arg; arg.reverse.each(&:call) end
def set_alarm n; sleep n; puts 'Ring!Ring!Ring!' end
reverse_call(
lambda { set_alarm 0.1 },
lambda { puts 'Good Night World' },
);
Promise.thenを用いたGoodMorningWorld
プログラムの記載順としてブロックも分かれていることと、thenはPromiseが成功した時に(続けて)呼び出す処理と解釈できるため、GoodMorningWorldであると判定できます。
const set_alarm = (resolve, n) => setTimeout( () => { console.log('Ring!Ring!Ring!'); resolve(); }, n);
new Promise(resolve => set_alarm(resolve, 0.1))
.then( () => console.log('Good Morning World'))
さいごに
「Hello World」の利点は、初めてのプログラムでも、"Hello World"を表示するプログラムだと推測できる点です。
z % Hello World
同期/非同期処理は、新しい言語では理解が難しい部分が多いですが、「Good Morning World」と「Good Night World」で共通理解が進むと嬉しいなと思います。
(参考) scriptタグについて
<script src="./set_alarm.js"></script>
<script src="./good_morning_world.js"></script>
<script src="./set_alarm.js" async></script>
<script src="./good_night_world.js" async></script>
定義部分は、(特定言語によらない)同期/非同期プログラミングの分かりやすい共通定義となるように考えましたが、おかしい点や、他にもっといい定義があればコメント歓迎です。
更にアドバンスドな題材や、他の言語の「Good Morning World」と「Good Night World」や解説記事が書かれると嬉しく想います。