Perlがくしゅう帳(Rubyも)

プログラミングの勉強会の参加記録や学んだことなど。 twitter ID : @tomcha_で活動しています。 最近は主にPerl関連の勉強会やコミュニティに参加しています。移転前のブログはこちら->http://ruby.doorblog.jp/

プログラミング言語C K&R著の演習問題を解答していく その1

昔、N88-Basicを写経して遊んだ後、チャレンジしたけれど関数とか用語で挫折したC言語に大人になってからのリベンジ。
C言語のバイブルと言われる、K&Rの「プログラミング言語C」を第一章から読み進めて演習問題を記録していく。

プログラミング言語C 第2版 ANSI規格準拠

プログラミング言語C 第2版 ANSI規格準拠

第1章

  • 1-1
#include <stdio.h>    
int main(){  
  printf("Hello,world!\n"):  
}

;を;に変えてコンパイル

1-1.c:4:27: error: expected ';' after expression
  printf("Hello,world!\n"):
                          ^
                          ;
  • 1-2
int main(){
  printf("hello,world\x");
}
1-2.c:4:22: error: \x used with no following hex digits
  printf("hello,world\x");
                     ^~
1 error generated.
  • 1-3
int main(){
  float fahr, selsius;
  int lower, upper, step;

  lower = 0;
  upper = 300;
  step = 20;

  fahr = lower;
  printf("fahr-selsius conversion table\n");
  while(fahr <= upper){
    printf("%3.0f %6.1f\n", fahr, ((5.0/9.0) * (fahr - 32.0)));
    fahr += step;
  }
}
  • 1-4
int main(){
  float selsius;
  int lower, upper, step;

  lower = 0;
  upper = 300;
  step = 20;

  selsius = lower;
  while(selsius <= upper){
    printf("%3.0f %6.1f\n", selsius, ((9.0/5.0) * selsius + 32.0));
    selsius += step;
  }
}
  • 1-5
int main(){
  float fahr;
  for(fahr = 300; fahr >= 0; fahr = fahr - 20){
    printf("%3.0f %6.1f\n", fahr, ((5.0 / 9.0) * (fahr - 32.0)));
  }
}
  • 1-6
int main(){
  int c;
  printf("%d",(c = getchar()) != EOF);
}
  • 1-7
int main(){
  printf("%d", EOF);
}
  • 1-8
int main(){
  int ns, nt, nnl;
  int c;
  ns = nt = nnl = 0;
  while((c = getchar()) != EOF){
    //getchar()はエンターが入力される度実行される。
    if(c == ' '){
      ++ns;
    }else if(c == '\t'){
      ++nt;
    }else if(c == '\n'){
      ++nnl;
    }else{
    }
  }
  printf("space => %d, tab => %d, newline => %d\n",ns, nt, nnl);
}
  • 1-9
int main(){
  int c;
  while((c = getchar()) != EOF){
    if(c == ' '){
      while(c == ' '){
        c = getchar();
      }
      putchar(' ');
    }
    putchar(c);
  }
}

第1回hommachi.rbに行ってきました

トークしました

第1回 hommachi.rbに行って、「はじめてのプログラミング」というテーマでトークしてきました。

トークの対象者は、プログラミングに興味があるけれども、プログラミングをした事がないビギナー向けの話です。hommachi.rbは初心者向けRubyの勉強会ですが、この初心者の定義2つ
- 「プログラミングをしたことが全く無い」 - 「何らかの言語でプログラミング経験はあるが、Rubyを使ったことが無い」 のうち、前者の方をターゲットにして話をしました。

今後も、第2回、第3回と続いていくと思いますが、もしまたトークをする機会があれば、この前者をターゲットにした何かを発表できたらなと思っています。

Railsのトーク

本日のメイントークは@ogomorさんによる「Ruby on Rails を環境構築からスタートして最終的には1つのwebサービスを完成させる」発表のレッスン1でした。

https://github.com/hommachirb/rails-example/blob/master/doc/lesson/001.md
今後も継続して参加すれば、階段を1ステップずつ上がるようにRuby on Rails の使い方がマスターできそうです。
Githubに手順1つずつに対応した変更履歴を上げられてますので、これをみながら予習復習するのも分かりやすくて良いですね。

自分用の今日のRailsの講義メモです。

  • Ruby ob Rails には設計の哲学思想があり、DRY(同じことを繰り返さない)とCoC(設定よりも規約)という2つがテーマ。この思想を理解した上でRailsを使うと、ただ書き写すよりも理解度が上がる。
  • CoC は今回作成したviewのテンプレートの命名で、navbarというナビゲーションバーの部品はページのhtml.erbからnavbarの呼び出しで組み込むことができる。''の付し方ルール(規約)だけでテンプレートの組み込みを実現していた。
  • config/routes.rb にアクセスした時の一番トップの挙動を書く。
  • コーディング作業は、rails server で立てておき、localhost:3000/rails/info/routes を見ながらすると捗る。

最後に

スタッフの皆さん、発表者のみなさん、おつかれさまでした。また次回よろしくお願いします。


*スライドでYAPC::ASIA 2015の宣伝をして、ヤプシー成功祈願活動もちょこっとだけしました

Perlのmapについて

Perl入学式in大阪第5回「map関数」についての補足事項です。

講義中に自分でテストコードを書いて試してからツイートをしたのですが、mapの話が懇親会で話題にあがり、@azumakuniyukiさんより、「mapは破壊的関数ちゃうのん?」と指摘があり、 その後にもう少し突っ込んでサンプルコードを試したところ、ツイートの「mapは、元のデータを書き換えるのではなく」は誤っていました。すみませんでした。

結論

map関数は「破壊的関数」であり、元の配列のデータは更新(上書き)されるケースがあります。

更新されるケースとは?

こういう事はコードを書いて実験するのが一番です。
4つのパターンのmap関数使用例を書いてみました。

mapのテスト

講義中にササッと書いてテストしたのはコード14行目の"a"のパターンです。
この使い方だと、確かに元の配列@aの中身は変更されず、別の配列@bに代入された結果だけが'.bak'を付加されているので、一見、「map関数は元の配列データを更新しない」様に見えます。

しかし、本当にそうだったのでしょうか? コード17行目"c"のパターン、コード18行目"d"のパターンともに元の配列@aaのデータは正規表現の条件式に当てはまるデータは".bak"を付加され、データが更新されていました。

"c"、"d"は正規表現の置換のコードです。$の値が置換されますね。それに対して"a"のパターンは$の値を”参照”はしているものの、代入する表現は含まれていません。代入をするパターンとして、コード15行目を試してみると、何と@aは元の配列のデータが更新されました。

この実験コードのテストから、$_は元の配列の個々のデータを参照しているので、当然書き換えれば元の配列のデータは書き換わることが分かります。ということは、「mapは破壊的関数で元のデータを更新する」という事ですね。

ただし、パターン"a"の様な、配列のデータを参照にとどめている場合は自動で更新されません。
なので、「mapは破壊的関数で元のデータを更新するが、中の式によって更新しないケースもあるので、ケースに応じて意図した通りに元の配列が更新されるか(又は更新されないか)を注意する必要がある」という事ですね。

結論その2

という事で、この話題も懇親会で色々講義の内容をワイワイ話した結果得られた知見でした。
懇親会、大事ですね。ざっくばらんに質問したり、話をしたりできる場ですので、講義だけでは聞けなかった事や、間違って解釈してしまっていた事が分かる場でもあります。また、サポーター陣も完全では無いので、今回のように間違いを発見できる場でもありました。

f:id:tomcha0079:20150117181044j:plain

f:id:tomcha0079:20150117195013j:plain

懇親会、大事。

Rubyでwebアプリひな形作成CLIツールを作ってみた

Rubyを書いてるの人の大半の目的はRailsの利用だと思うのですが、自分はRails newを試した位しか、した事がありません。
まあ、今のところ趣味で作るサイトやwebアプリにはRailsは多機能過ぎて、もっぱらSinatraHamlで挙動を確認しながら色々試しています。良い点として、自分で必要なGemを吟味して追加する必要があるので、それぞれどの部分が何をしているかが把握できる点ですね。
また、Sinatraを使ったwebアプリの作り方は

Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)

Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)

で1から手取り足取り解説されてて、この本を写経してやり方を覚えればwebアプリが作れるようになります。非常に良い本でおすすめです。

本題

で、いくつかのwebアプリを作っては捨て、作っては捨てをしていると、初期ファイルを書くのが非常に面倒になってきました。
Rails newでひな形を自動でガガガーっと作ってくれるみたいな、自分用のツールが欲しい!と思ったので、CLIツール「Sinatras(名前は超適当)」の作成にチャレンジしてみました。
パーフェクトRubyの第15章を参考に(というか、ほとんど写経)して、何とか完成。

パーフェクトRuby (PERFECT SERIES 6)

パーフェクトRuby (PERFECT SERIES 6)

今のところnewコマンドしかありませんが、

sinatras new <appname>

で、appnameで指定したアプリ名でディレクトリを作成し、

  • 各ディレクトリをよしなに作成
  • Gemfile作成
  • config.ru作成
  • アプリ名.rb作成
  • ビュー用テンプレindex.haml,layout.haml作成
  • git initの実行

等をしてくれます。
実際に実行した様子はこんな感じ

f:id:tomcha0079:20150110200423p:plain

作成されたファイルは・・・

f:id:tomcha0079:20150110200435p:plain

※ 追記
ファイルが作成されたら、もちろんrackupできます。 127.0.0.1:9292 でブラウザからこの画面が出るはず。

f:id:tomcha0079:20150110221147p:plain

f:id:tomcha0079:20150110221152p:plain

あとはviewsの中のhamlファイルをいじれば、簡単なwebページが作れます。cssを作った時はpublicフォルダへ保存しましょう。

パーフェクトRubyを読んで

まだ、頭から読み進めてる途中ですが、CLIツールは第15章に書かれている通りに読み進めていけば簡単にGemファイルを作る事ができました。
作ったGemファイルをインストールする事により、どこでも「コマンド」として実行できます。
今回勉強になったのは、前のブログで書いたモジュールやクラスの機能分離方法の実例が学べた点です。作る過程で体験できるので非常に良いです。

また、この本は「たのしいRuby

Rubyに入門した次に、脱初学者向けとして読むのが丁度良いと思います。
気をつける点は、若干ソースコードに誤植があるのと、おそらくスペースの都合上、コードの一部を省略している点です。
どの部分のコードなのかは、出版元サイトから掲載されているコードがダウンロード出来るので、それを直接見るのがベストです。

エンジニアリングとは

プログラミングの醍醐味は、モノ作りをして完成した時の達成感や動いた時の感動ですが、もうひとつ大事な事として、エンジニアリングによる作業の短縮効果でしょうか。実用的なアプリを作ることによって、今までとはまた違った喜びの体験ができました。

とにかく、rails newみたいにSinatraのアプリが作れる様になりました!やった!
ソースコードGithubにあります。

https://github.com/tomcha/sinatras

アドバイス等があれば、お手柔らかによろしくお願いします。

パーフェクトRubyを読んでの疑問

冬休みの課題として、オレオレCLIツールが書きたいと思ってググっていたら、なんと今読み進めている「パーフェクトRuby」の第15章でCLIツールの作り方の記事があったので、それを読みながら書いている時の疑問のについてメモ。

パーフェクトRuby

パーフェクトRuby

記事では「Todo」というアプリを、bundlerをつかってGemを作る構成。 Gemを作る為のファイル構成の中で、中心部分は

./bin/todo
./lib/todo.rb
./lib/todo/command.rb
./lib/todo/version.rb
./lib/todo/command/opions.rb

とファイルが構成されていて、"./bin/todo"を実行すると、"./lib/todo"が読み込まれ、そこでrequireしている"./lib/"以下のファイルを読み込んでいる。 疑問点は、"./lib/"以下のファイル全てで「module Todo」とモジュール宣言されていて、そのモジュール内のほぼ全てで「Class Todo」が書かれていたが、書かれたモジュールを「include」も「extend」もしていなかった点、そして、同じクラス名で書かれていた点でした。

最初は誤植かな?とも思ったのですが、その様子は無く、この書き方がセオリーだとしたら、何なのか?をサンプルコードで試してみる事にしました。 1つのファイルに延々と色々な機能のコードを書くと肥大化してしまうので、ファイルを分けて書く点はその通りなのですが、クラス名を同じにしている点について仮説を立てて考察してみました。

別のファイルで同じクラス名の定義を書く

直感では、これはオープンクラスを利用して、「1つの仕組み(クラス)」を「機能ごとのファイルに分けて書いた」手法なのではないかという事。 メインのファイル"./lib/todo.rb"が、次々にClass Todoにメソッドを追加していっているという理解で良いのか実験をしてみました。 実験用のコードは、「衣笠丼を作る」というコード。

Class Kinugasadon に「油揚げを投入するメソッド」「葱を投入するメソッド」「玉子を投入するメソッド」「ご飯に乗せるメソッド」を順番に追加して、かつ、オープンクラスであれば同名のメソッドであれば後から追加したものが優先される特性を利用して、「フルーツを投入する」というメソッドを追加して上書きされるかも実験してみました。

f:id:tomcha0079:20141231183959p:plain

実験結果から、多分、1つのクラスを分解する方法として、オープンクラスを利用するセオリーなのだろうという結論です。

includeもextendもされない同じ名前のmodule

さっきの"age.rb"、"negi.rb"、"age.rb"の3つのファイルのmodule宣言部分をコメントアウトして、class宣言だけのファイルにし、実行ファイルのkinugasa.rbのオブジェクト生成コード部分を

kinugasa = Kinugasa.new

と変更して同じように実行してみた結果は、

f:id:tomcha0079:20141231184140p:plain

うーん、特に変化なくきちんと実行されました。
どうやらmodule宣言は無くても動くようです。そもそも、moduleをmix-inして使うときに必要な「include」も「extend」もしてません。
実験結果と状況からの自分なりの答えとしては、module宣言をした理由は「名前空間」の利用の為なのでしょうか。自分だけの小さなプログラムなら全体を把握できますが、他人が見た時とかにちゃんと外部ファイル管理されているクラスという事を明示させ、わかりやすくする為にmodule宣言をしているのでしょうか?
今のところはその様な理解という事でブログ記事を書いてみました。

なんかPerlのpackage宣言する効果と似てるなーと思ったり。
あ、あと、衣笠丼に苺とかバナナを入れてはいけません。入れるとエラい事になりますのでご注意を。

素数とJavaと実行速度

はじめに

最近、素数にちょっとした縁があったので、素数を計算するプログラムをRuby書いてみました。
おそらく、もっと良いロジックやひょっとするとライブラリもあるかとは思ったんですが、なんとなく自力で書けるかなーと試してみたかったので。
あと、数学も詳しくないので、細かい表記が間違っていたらゴメンナサイ。

基本方針

  • 小さい数から判定していく(素数か否かを検査する対象をnとする)
  • 素数という事は、素因数分解できないはず->という事は、今まで素数と判定した数値群で割った時、余りが0ではない。
  • 上記の数値群の小さい方から判定していき、数値群の数がnの平方根を超えたら、余りが0でない事が確定。(割り切れる場合は、それまでに余り0が出てるはず)

Rubyのコード

基本方針の仕様に基づいて、Rubyで書いてみたコードがこちら。
素数判定の部分をメソッドにして呼び出し、素数と判定されたものは数値群(配列)にどんどん追加していきました。
また、判定は2と3は初期値に設定しておき(2は配列に1つ以上の要素を入れる為、3は2が既に平方根の値を上回っているので)、5からメソッドで判定しています。
10万回判定すると終わります。

prime number ruby

Javaでつまずいたこと

で、簡単なコードなので最近文法を勉強し始めたJavaでも書いてみて、LL言語コンパイル言語がどれくらい実行速度が違うのかを試してみました。そのとき調べた事をまとめてみました。

Javaの配列は、固定長

PerlRubyでは何も考えずに配列を使っていましたが、Javaでは配列の中身に対して型を宣言するのはもちろんの事、配列の長さも宣言時、または初めて使う時に長さも指定する必要があります。
いくつ素数があるのか分からないし、最大限の目安で要素数確保するのも不格好なので、ググって調べた所、可変長の表現できる配列風クラスArrayListクラスがあったのでこれを使ってみました。
少し気をつけないといけないのは、配列は変数であるのに対して、ArrayListはクラス(使うときはインスタンスオブジェクト)なので、使い方も似て非なるところですね。
一番「そんな事までしないといけないのかー」と思ったのは、ArrayListはオブジェクトを格納するコレクションなので、1とか2とか、基本形の値を格納出来ない事でした。じゃ、どうやって格納するの?というと、intの"値"を数値のようなIntegerオブジェクトに変換して、オブジェクトとして格納、取り出す時もIntegerオブジェクトのメソッドで値を取り出す、という一手間が必要でした。(35行目、28行目あたりです)
それと、ArrayListのオブジェクトを引数で渡す時は、受け取るメソッドの仮引数宣言部分でも、中身の型まで宣言しておかないと警告が出る部分ですね。ここまで厳密だとは知らなかったので勉強になりました。

prime number java

速度を試す

では、Javaのコードはjavacコマンドでコンパイルして、Rubyと実行速度を比べてみましょう。

Rubyの実行結果

f:id:tomcha0079:20141221235153p:plain

Javaの実行結果

f:id:tomcha0079:20141221235157p:plain

実行速度で約2倍くらいでしょうか。(実際は、最初に書いていたロジックがもっとドン臭くて全探索に近かったので、11倍くらいの圧倒的な差でした。) という事は、アルゴリズムのオーダによって、言語の速度差が加速度的に増すということですね。 効率良いロジック、大事。

Javaは実行速度が遅いというイメージでしたが、LLよりはやはり速かったです。

ここまでくれば・・・

じゃぁ、Cとかだとどこまで速くなるんだ? 好奇心にまかせて、初めてC++を書いてみました。可変長配列とか、Cだと大変そうな気がしたので。

prime number c++

C++の印象は、Javaほど書き方が面倒ではない印象でした。
で、実行速度は・・・

f:id:tomcha0079:20141221235202p:plain

Javaとあまり変わらない・・・ってか、最初のドン臭いコードではJavaより少しだけ遅かった・・・。
もっと圧倒的な差が出ると思っていたのですが、以外でした。
しかし、この結果には少し注意点があって、Javaのコードの計測はVMJavaを実行させる前準備作業)の時間が考慮されていないコードになっています。1回めのターミナルからコマンド打って実行した時は、書いたコードが走り出すまで少しタイムラグがあります。
もちろんC++ではこの時間は無いので、

C++の実行時間 ≒ VM起動時間+Javaの実行時間 < Rubyの実行時間

という結果が妥当な様です。 文法も実行結果も、色んな言語を比較してみるのも面白いですね。

第1回なにわPerlを開催しました

なにわPerl主催のTomchaです。

平成26年12月13日、joe'sビジネスセンターさんの場所をお借りして、第1回なにわPerlを開催しました。

大事なこと

会場をお貸し頂いたjoe'sさんでは、VPSサービスのホスティング等、自作webアプリをインターネット上に公開する為のサーバーホスティング等をされています。オレオレserverが欲しいなぁ等、興味のある方はwebサイトにプランの説明などが書いてありますので、ご覧下さい。

なにわPerl

初開催ながら、飛び入りも含めて6名の参加でRubyJavaC#やGoの話題も出ながら、わいわい、もくもくと楽しく作業が出来ました。 みなさんが持ち寄ったお題は、

  • Mojoliciousを使ってのwebサービス開発の勉強
  • Perl入学式第5回、第6回の予習
  • ご当地色豊かな色んな物比較モジュールの開発
  • とあるPerlモジュールのGIthub上でのプルリクエスト
  • Perfect Rubyの読書&勉強
  • Rubyを使ってのbot開発

などなど、言語や目的も多岐にわたっての内容でした。

特に要望が多かったのが、Perl入学式の開催間隔が2ヶ月で長いので、間を埋める何かが欲しいとの事だったので、Perl入学式のサブイベントとしては丁度よかったのではないかと思います。
また、Mojoliciousのペアプロ風マンツーマンでの解説があったり、普段の入学式は講義形式なので、個別に聞きづらい事が聞ける機会になったりと、なかなか有意義なイベントになりました。

こんな雰囲気で開催しました

イベントの様子は、togetterでまとめました。 -> なにわPerlのまとめ

懇親会

懇親会は会場近くの駅前第3ビルの居酒屋で開催されました。
Goの話やPerlRubyの話から、キセルの話まで幅広く楽しく交流を深める飲み会になりました。

なにわPerlの今後について

第1回が割りと好評だった事と、自分的にも参加者さん的にも、丁度今のところ関西に無いタイプの勉強会?イベント?だったので、今後も継続して続けていきたいと思っています。
DoorKeeperのコミュニティにとりあえず登録しておくと、次回開催情報がメールで受け取れますので、興味のある方は登録してみて下さいね。

なにわPerlのコミュニティはこちら↓

http://naniwaperl.doorkeeper.jp/