よこのよこのよこがよこである確率(後編)

2019-12-22
よこのよこのよこがよこである確率(後編)





......という題で議論しようと思ったけど、数学が高校レベルから進展してないせいで苦しくなった人の話(続き)



********************


かなこ「起きて」
よこ「うーん......」
かなこ「起きなさい」
よこ「うーん......」
かなこ「昨日のプログラミングの話をしてくれる約束だろう?」
よこ「うーん......」
かなこ「スペルカード!!風神様の神徳ッ!!!!!!(シュババババ)」
よこ「あーーおはようございますっ!!」


********************


よこ「まず、問題の確認から」
かなこ「うむ」

ーーーーーーーーーーーーーーーーーーーー

A - お賽銭


問題文
きゅうりバー神社では、毎朝決まった時間にお賽銭箱の中からお賽銭を取り出し、小銭の数を数えています。
今朝はA銭硬貨のみがちょうどB枚だけ入っていました。今朝の分のお賽銭の合計金額S(銭)を計算してください。

制約
0 < A < 1,000
0 < B < 10,000


入力
入力は以下の形式で標準入力から与えられる。
A B

出力
合計金額S(銭)を出力せよ。


入力例
100 5

出力例
500

100銭硬貨が5枚で、合計金額は500銭です。

ーーーーーーーーーーーーーーーーーーーー


よこ「A*Bを出力するだけの問題だったよね」
かなこ「そうだな」
よこ「で、まずC言語でこう書いた」


main(a,b){if(scanf("%d%d",&a,&b)+printf("%d",a*b)){}}


かなこ「私の知っているC言語ではないが......」
よこ「そんなことない。まず次のようなコードがあったとする。左端の数字と棒は行番号だから無視して」


1| #include<stdio.h>
2| int main()
3| {
4| int a=0,b=0;
5| scanf("%d %d",&a,&b);
6| printf("%d\n",a*b);
7| return 0;
8| }


かなこ「これは、わかるぞ」
よこ「いいよね。main関数(2行目〜)でまず2つの変数a,bを宣言・初期化して(4行目)、標準入力から整数を空白区切りで2つ読み込んでa,bに格納し(5行目)、その積を標準出力に書き出す(6行目)。最後にmain関数の返り値として0を与えて終わり(7行目)」
かなこ「これをどうやったら上のコードになるんだ?」
よこ「まず、scanf()やprintf()などの関数が入っているヘッダファイル "stdio.h" を最初にinclude(このヘッダファイルの中の関数などを使いますよ〜という宣言)してるけど、実はこれ、CuCumberの実行環境ならなくても動く」
かなこ「あら、そうなの」
よこ「ので、消す。あと2行目でmain関数の型をint型(4バイトの整数型)としているけど、型名を書かない場合int型とみなされるので、いらない。さらに7行目の return 0; は、書かなくてもint型のmain関数なら return 0; があったとみなされるらしい」
かなこ「よく分からないが、色々なくても動いてしまうんだな」
よこ「C言語の全てのバージョンと環境でそうだとは限らないから、気をつけてね。これらを反映するとこうなる」


1| main()
2| {
3| int a=0,b=0;
4| scanf("%d %d",&a,&b);
5| printf("%d\n",a*b);
6| }


よこ「もう少し行ってみようか。まず変数a,bについてだけど、これを main(a,b) という風にしてmain関数の引数にしてしまう」
かなこ「できるのか」
よこ「よく知らないけど、できるらしい。ちなみにこのように型を何も書かずに main(a,b) とすると、aもbもint型の1として初期化される」


1| main(a,b)
2| {
3| scanf("%d %d",&a,&b);
4| printf("%d\n",a*b);
5| }


よこ「で、ここからが本題なんだけど、scanf()もprintf()も関数だよね?」
かなこ「そうだな」
よこ「関数ということは、sin()やmax()みたいに、引数を与えると何かしらの値が返ってくるものであるよね?ただscanf()やprintf()は少し特殊で、値を返すと同時に標準入出力を通してデータを読み書きするけど」
かなこ「具体的には、scanf()やprintf()はどんな値を返すんだ?」
よこ「scanf()は、読み込みに成功した%dなどの数をint型で返すらしい。例えば今回なら scanf("%d %d",&a,&b); でaもbも読み込みに成功した場合に2が返ってくる。printf()は、出力されたバイト数が返ってくる。例えば printf("konpaku"); だと、konpakuという文字列は全部で7バイトだから7が返ってくる。printf("あ"); なら、マルチバイト文字だから1文字だけど3が返ってくる」
かなこ「なるほど」
よこ「これを踏まえて、次のような記述があったとしよう。a,b,nをint型の変数として」


n = scanf("%d %d",&a,&b) + printf("%d\n",a*b);


かなこ「nにscanf()の返り値とprintf()の返り値の和が代入される、と?」
よこ「そうそう。scanf("%d %d",&a,&b) による標準入力と printf("%d\n",a*b) による標準出力が行われた上で、それぞれの返り値の和がnに代入される」
かなこ「なるほど」
よこ「ところでC言語には"セミコロンレス"というコード遊びがある。セミコロン";"を使わずにコードを書くというもの」
かなこ「いや、知らないが」
よこ「命令の後には大抵セミコロンを打たなければならないから不可能に思えるけど、みんなが知っているこの記述にはセミコロンが登場しない」


if(条件式)
{
//何もしない
}


かなこ「確かにそうだな」
よこ「というわけで、今回はscanf()とprintf()を演算子"+"で繋いでif文の条件式として与えてしまい、セミコロンレスを成功させている。こんな形になる」


if( scanf("%d %d",&a,&b) + printf("%d\n",a*b) )
{
//何もしない
}


よこ「if文の条件式として scanf(省略) + printf(省略) が評価され(るついでにa,bの入力とa*bの出力が行われ)、評価した結果(足し算の結果が0でなければTRUE、0ならばFALSE)がどうあろうと、続く{}が空っぽだから結局何もされない」
かなこ「おお......」
よこ「これらを踏まえると、元のコードはこうなる」


1| main(a,b)
2| {
3| if( scanf("%d %d",&a,&b) + printf("%d\n",a*b) )
4| {
5| //何もしない
6| }
7| }


かなこ「うむ」
よこ「あとは細かいところだね。まずscanf()の引数で "%d %d" となっているけど、間の空白はなくても正しく読み込まれるみたい。さらに、これはCuCumberの判定システムの話なんだけど、出力の最後に改行がなくても正解判定が取られるので、printf()の出力文字列の最後の改行 "\n" はいらない。最後に余計な空白や改行を切り詰めると、最初に出てきたコードになる」


1| main(a,b){if(scanf("%d%d",&a,&b)+printf("%d",a*b)){}}


かなこ「おお。すごい。ところで、こういう書き方をすることに意味はあるのか?」
ようむ「あんまり無い!」


********************



おしまい



********************



あとがき


こんにちは、よこです。早いもので一応暦の上ではクリスマスまであと僅かということになっていますね。こういう機会はあまりないので、自己紹介をさせてください。僕をよく知っている人は適当に読み飛ばしてください。


私は2年生に上がるときにGUTに入りました。現在は降年したため余暇の多い1年生をやっています。その余暇で主に音楽ゲーム(チュウニズム)やプログラミング(競技プログラミングとゲームプログラミング)、音楽作り(東方アレンジ中心)などをして大学生活を楽しんでいます。

東方に入ったのは音楽ゲームがきっかけで、ちょうど1年前くらいに高校の友達に布教されてドハマりしてしまいました。音楽作りを始めたのは、もちろん魅力的な東方原曲に触れられたのがきっかけです。それまでも音楽が好きでしたが、東方原曲が「一人の手によって作られている」ことを知って音楽制作を身近に感じられたのが大きかったと思います。競技プログラミングも、これまた別の友達に誘われて、実力はアレですが高校生の頃からやっていました。「コンテストに出てレートを上げる」ことよりも「人が書かない書き方をする・人が書かない言語で書く」ことの方に関心があり、AtCoderのABCのA問題を色々な言語で何周もしたり、マイナー言語のLanguage Ownerで1位を取ったりというような方向ばかりにエネルギーを費やしています。また、ゲームプログラミングと始めに書きましたが、東方の影響を受けてオリジナルのシューティングゲームを個人で作っています。こちらは始めたばかりの趣味で、これからの発展に期待をしています。


さて、記事のあとがきなので最後に記事の話もします。もう少しだけお付き合いください。今回実はネット記事を書くのは初めてで、どんな内容にするか悩みました。とりあえずプログラミングの話が一番書きやすそうだ、ということでそうなっています。題名は内容を決める前に適当に決めたものです。前編は「簡単な問題を多言語で解いてみた!」という話で、コードの説明が重そうだったので後編に回しました。そして今回の後編なのですが、ごめんなさい、最初のC言語のコードの解説をしたところで力尽きてしまいました...... Twitter ( @dolyplpl )まで要望を飛ばしてく下されば個別で説明するか、なんなら別記事を書きますので言ってください。また、今回の説明もそうですが、まだまだ各言語に対する理解が足りていないので、間違いや補足などあれば遠慮なく指摘していただけると助かります。読んで分からないところがあったら、それは私の説明が悪いのでどんなことでも遠慮なく聞いてください。感想とかもいただけると嬉しいです。というわけで、ここまで読んでくださってありがとうございました。これからもよろしくお願いします。



よこ