2013年2月14日木曜日

7つの言語7つの世界 [Io 1日目]

Io 1日目

Rubyが終了して次はIoという言語。
正直今回始めて聞いた。
自分の世代だと雑誌のほうをを先に思い浮かべてしまう。

オブジェクト指向ではあるがプロトタイプベース言語ということで、
思想はJavaScriptと似ているらしい。

インストール

とりあえず、Ubuntuにインストールしてみる。
いつもどおりapt-getでと思ったら、見当たらない。packageの中に入ってないらしい。
しょうがないのでネットで情報を探してみる。
お約束のように違うIoの情報が大量に引っかかる。

やっとあった。
と思ったらUbuntuで同じことをやってる方がいらっしゃる
古い本だしそりゃそうだよね。
なるべくほかのページは見ずにInstallの方法だけこぴらせていただく。


$ sudo apt-get install build-essential
$ sudo apt-get install cmake
$ wget http://github.com/stevedekorte/io/zipball/master
$ unzip master
$ cd stevedekorte-io-8956a60
$ mkdir build
$ cd build
$ cmake ..
$ make


本当はこの後make installと続くのだけど、なるべくsrc makeしたものをいれたくない
幸いLIBRARY_PATH等は設定しなくても、./_build/binariesのioは実行できたので、
とりあえずこのまま進めて何か問題があったら考えるとしよう。

試してみよう

ファイルからIoのプログラムを実行せよ


#!./stevedekorte-io-8956a60/build/_build/binaries/io     
"Hi ho, Io" print
$ ./HelloWorld.io
Hi ho, Io

拡張子が.ioなのかは自信無。
簡単に実行してくれるシバンは素敵。

スロットの名前を指定して格納されているコードを実行せよ


Io> MyObject := Object clone      
==>  MyObject_0x8277630:
  type             = "MyObject"

Io> Car := Object clone
==>  Car_0x83668b8:
  type             = "Car"

Io> Car drive := method("Running" println)
==> method(
    "Running" println
)
Io> Car drive
Running
==> Running
ほぼほぼサンプルのままだけどこれでいいのかな?

2013年2月11日月曜日

7つの言語7つの世界 [Ruby 3日目]


Ruby3日目

今回はメタプログラミングの話が主。RubyOnRailsにも興味があるので気になる話。
しかし、概要をサラッと流されているので深くは理解できず。
ただ、method_missingの話はおもしろい。
いかにRubyがプログラマに自由を与えているかのいい例だと思う。
(実際にこんなトリッキーな手法を使うかどうかは置いておいて。。。)
エラーの動作まで書き換えられる思想はすごい。

試してみよう

eachメソッドがCsvRowオブジェクトを返すようにCSVアプリケーションを変更せよ。そのCsvRowのmethod_missingを使って与えられた見出しの列を返すようにせよ。


#!/usr/bin/ruby                                        
                                                       
module ActsAsCsv                                       
                                                       
  def self.included(base)                              
    base.extend ClassMethods                           
  end                                                  
                                                       
                                                       
  module ClassMethods                                  
    def acts_as_csv                                    
      include InstanceMethods                          
    end                                                
  end                                                  
                                                       
  module InstanceMethods                               
                                                       
    def each                                           
      @csv_contents.each do |contents|                 
        yield contents                                 
      end                                              
    end                                                
                                                       
    def read                                           
      @csv_contents = []                               
      filename = self.class.to_s.downcase + '.txt'     
      file = File.new(filename)                        
      @headers = file.gets.chomp.split(', ')           
      file.each do |row|                               
        @csv_contents << CsvRow.new(row.chomp.split(',\
 '), @headers)                                         
      end                                              
    end                                                
                                                       
    attr_accessor :headers, :csv_contents              
                                                       
    def initialize                                     
      read                                             
    end                                                
  end                                                  
end

class CsvRow                                           
  def initialize( content, headers )                   
    @content = content                                 
    @headers = headers                                 
  end                                                  
  def method_missing(name, *args)                      
    idx = @headers.index(name.to_s)                    
    if idx != nil                                      
      @content[idx]                                    
    end                                                
  end                                                  
end                                                    
                                                       
class RubyCsv                                          
  include ActsAsCsv                                    
  acts_as_csv                                          
end                                                    
                                                       
m = RubyCsv.new                                        
m.each{|row|puts row.one} 



うえでは、method_missingはトリッキーとかいたけど、
こうやって見てみるとユーザ側のコードはかっこいいかも。
結局フレームワーク側で多少トリッキーなコードを書いても
ユーザのコードが見やすく生産性が上がればOKと言う発想なのかな?

7つの言語7つの世界 [Ruby 2日目]

Ruby2日目

今回はCollectionやコードブロックの説明。
この辺が出てくるとRubyっぽいかも。
Rubyは多重継承を避けるためにMixinを使ってる。
Mixinと言う単語は始めて聞いたが、多重継承を避けるための工夫としておもしろい。
各オブジェクト指向言語での多重継承の仕様に関して時間があったらここでまとめてみよう。

試してみよう

最初にeachだけを用いて16個の数値と4個の数値の配列の中身を同時に出力せよ。次に、同じ異をEnumerableのeach_sliceを用いて実行せよ

each
#!/usr/bin/ruby  
a = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]   
b = [20,21,22,23]                             
                                              
i=0                                           
a.each {|x|                                   
  puts x                                      
  puts b[i%4]                                 
  i+=1                                        
}

each_slice
#!/usr/bin/ruby                             
                                            
a = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] 
b = [20,21,22,23]                           
                                            
a.each_slice(4) {|ar|                       
  ar.each_with_index{|x,i|                  
    puts x                                  
    puts b[i]                               
  }                                         
}

あってるのかな?いまいち出題の意図がつかみきれてない気がする。

Treeクラスを改造してinitializerにハッシュと配列が入れ子になった構造を指定できるようにせよ。


#!/usr/bin/ruby                                           
class Tree                                                
  attr_accessor :children, :node_name                     
                                                          
  def initialize(child_list, key=nil)                     
    @children = []                                        
    if key == nil                                         
      child_list.each{|root_key,root_value|               
        @node_name = root_key                             
        child_list = root_value                           
      }                                                   
    else                                                  
      @node_name = key                                    
    end                                                   
    child_list.each{|other_key,other_value|               
      @children.push(Tree.new(other_value,other_key))     
    }                                                     
  end                                                     
                                                          
  def visit_all(&block)                                   
    visit &block                                          
    children.each{|c| c.visit_all &block}                 
  end                                                     
                                                          
  def visit(&block)                                       
    block.call self                                       
  end                                                     
end                                                       
                                                          
init_list={'grandpa' => {'dad' => {'child 1' => [], 'chil\
d 2' => []}, 'uncle' => {'child 3' => [], 'child 4' => []\
 }}}                                                      
ruby_tree = Tree.new(init_list)                           
puts "Visiting a node"                                    
ruby_tree.visit {|node| puts node.node_name}              
puts "visiting entire tree"                               
ruby_tree.visit_all {|node| puts node.node_name}         

root nodeの扱いに苦労した。


Grepをかけ

#!/usr/bin/ruby                                           
                                                          
r = Regexp.new(ARGV[0])                                   
ARGV.shift                                                
                                                          
ARGV.each do |filename|                                   
  i=0                                                     
  File.open(filename, "r") do |io|                        
    while l=io.gets                                       
      if l=~r                                             
        puts filename+" @ "+i.to_s()+" : "+l              
      end                                                 
      i+=1                                                
    end                                                   
  end                                                     
end                                                       

2013年2月10日日曜日

7つの言語7つの世界 [Ruby 1日目]

7つの言語7つの世界という本を読んだ。
7種類の言語を紹介している本で、一つ一つの項目は短いが
それぞれ言語の特徴や考え方がわかりやすく紹介されている。

自分は以前から関数型言語等いつも使っている手続き型言語やオブジェクト指向
以外の言語に興味を持っており、いい機会なので
紹介されている言語に関してそれぞれ練習問題をやってみようと思う。

最初はRuby
この本の中では一番メジャー言語だと思われる。
以前から簡単なスクリプトは書いたことがあったので、多少は慣れている。

まずは、開発環境のInstall
今回はUbuntu12.04を使用する。

何も考えずに打ってみる。

$ irb
プログラム 'irb' は以下のパッケージで見つかりました:
* ruby1.8
* ruby1.9.1
次の操作を試してください: sudo apt-get install <選択したパッケージ>

Ubuntuさん相変わらずの親切設計ということで、おっしゃるとおりinstall

$ sudo apt-get install ruby1.9.1

さくっとインストール完了

試してみよう

練習問題をやってみる。

文字列Hello, worldを出力する。

$ irb
irb(main):001:0> puts "Hello, world"
Hello, world
=> nil


文字列Hello, Rubyの中のRubyという単語のインデックスを検索する

$ irb
irb(main):001:0> "Hello, Ruby".index("Ruby")
=> 7


自分の名前を10回出力する。


$ irb
irb(main):001:0> for i in 0..9 do puts "itokin" end
itokin
itokin
itokin
itokin
itokin
itokin
itokin
itokin
itokin
itokin
=> 0..9

for文はrangeオブジェクトが帰ってくるのをはじめて知った。


 文字列"This is sentence number 1"の1から10までカウントアップしながら10回出力する。

 

$ irb
irb(main):001:0> for i in 1..10 do puts "This is sentence number #{i}" end
This is sentence number 1
This is sentence number 2
This is sentence number 3
This is sentence number 4
This is sentence number 5
This is sentence number 6
This is sentence number 7
This is sentence number 8
This is sentence number 9
This is sentence number 10
=> 1..10

乱数を選択するプログラム。


#!/usr/bin/ruby

x=rand(10)
print "Please Input number: "

while STDIN.gets
  if $_.to_i == x
    puts "Success!"
    break
  elsif $_.to_i < x
    puts "Bigger than yours." 
  elsif $_.to_i > x
    puts "Smaller than yours."
  end
  print "Please Input number again: "
end

$ ./rand.rb
Please Input number: 7
Smaller than yours.
Please Input number again: 4
Bigger than yours.
Please Input number again: 5
Bigger than yours.
Please Input number again: 6
Success!

普段使っている言語に近いだけあってそれほど難しくなくかけた。

2013年2月3日日曜日

リンクエラーあるある

自分的リンクエラーのあるある。
  1. -lstdc++のつけ忘れ
  2. extern C
  3. リンクの順序



-lstdc++のつけ忘れ


というか、そもそもつけることを知らなかったり。
初めてC++をやってみようと思いネットで適当に見つけた
以下のようなサンプルをコンパイルしようとして最初に陥る罠
自分も学生時代に踏みました。

#include <iostream>

using namespace std;

int main()
{ 
    cout << "HelloWorld" << endl; 
    return 0; 
}

一番シンプルなHelloWorldですが,これを打ち込んで以下のようにコンパイルしようものなら。
$ gcc test.cpp -o test_out
test.cpp:(.text+0x14): undefined reference to `std::cout'
中略
collect2: ld はステータス 1 で終了しました

となります。
えー!ですよ。涙目で作成したプログラムを見直すのですが、間違いもなく。
そもそもコピペだし。
当時はコンパイルとリンクの差もよくわかっていなかったので、はまりました。
結局以下のように-lstdc++をつければ解決なのですが。

$ gcc test.cpp -o test_out -lstdc++

標準ライブラリを明示的に指定しないと使えないってどうなん。と今でも思います。
この前C++の誕生の歴史を本で読んでちょっと納得しかけましたが、まだ不満は残ってます。


extern C


C++はC言語の拡張として開発されてたためC言語の文法がそのままとおります。
と言う話はよく聞きますが、実際混ぜて作ってみるとハマります。
C++からCを呼び出す場合

C言語ファイル

#include <stdio.h>
#include "linktestlib.h"

void print_lang()
{
    printf("Hello C\n");
}

C言語ヘッダ

void print_lang();
呼出側のC++


#include "linktestlib.h"

int main()
{
    print_lang();
    return 0;
}

これをコンパイルすると。
$ gcc test.cpp linktestlib.c -o linktest
/tmp/cczMjmEx.o: In function `main':
test.cpp:(.text+0x7): undefined reference to `print_lang()'
collect2: ld はステータス 1 で終了しました

見事にリンクエラーですね。
これは、C++には関数のオーバロード(同じ名前でも引数によって別の関数として扱う文法)があるので、
関数名を引数も含めた記号にして(manglingして)扱っています。
一方C言語は、関数名だけで扱っています。
結果、リンカは単純に同じ名前の関数を探しているだけなので、C++の関数からC言語の関数が見つけられなくなります。
この現象はextern CでコンパイラにC言語だよと教えてあげると回避できます。
#ifdef __cplusplus
extern "C"{
#endif
void print_lang();
#ifdef __cplusplus
}
#endif

#ifdef __cplusplusで囲っているのはC言語のときにコンパイルエラーを回避するため。
Headerの中を囲ってますがもちろんincludeしているC++側で囲ってもOKです。
ただ、Header側で囲ってあげるのがやさしさですよね。
自分で書いたときは気づきますが、人からライブラリをもらったときは
たまに"あれっ"てなるときあります。


リンクの順序


リンクには実は順番がありますという話。
以下のようなコードがあったとします。
test.c

#include "liba.h"
int main(){
    func_lib_a();
    return 0;
}

liba.c
#include <stdio.h>
#include "liba.h"

void func_lib_a()
{
    func_lib_b();
}

libb.c
#include <stdio.h>
#include "libb.h"

void func_lib_b()
{
    printf("lib b called.\n");
}

Headerはプロトタイプ宣言だけなので省略
また、liba.cとlib.cは静的ライブラリとしてコンパイルされているとします。

つまりは、静的ライブラリAが静的ライブラリBの関数を参照しており、
main文が含まれるtest.cからライブラリAの関数を呼び出した状態です。

数珠つなぎにリンクされるので、当然両方をリンクする必要があります。
なので、両方をリンカに渡してやると。

$ gcc -o test test.c -L. -lb -la
./liba.a(liba.o): In function `func_lib_a':
liba.c:(.text+0x13): undefined reference to `func_lib_b'
collect2: ld はステータス 1 で終了しました

となり、リンクエラーとなります。
一方
$ gcc -o test test.c -L. -la -lb

は成功します。
一般的なUNIX系のオプションは並び順を問われないことが多いので忘れがちですが、
リンカは順番にシンボル解決が行われるため、順番が重要になります。
参照されるものは後です。

2013年2月2日土曜日

Coders at work

Coders at workを読んだ。
有名なHacker15人のインタビュー集。

一番の有名どころはThe art of programming(というかTexの?)のクヌース先生、
他にもHaskellの作者やNetscapeの製作に関わった人など盛りだくさん。
インタビュアー(=作者)もCommonLispの有名なHackerということで、
関数型言語の開発に関わっている人が多い印象。
ちょっと年齢層は高め。

インタビューの手法として始めてプログラミングに関わったところから聞き始めプログラミングの経歴、手法、以前と最近のプログラミングで何が変わったかなどを順番に聞いていく。
The art of programmingを持っているか?という質問があったり、あなたは職人ですがアーティストですか?科学者ですか?と言う質問があったり。なかなかバラエティに富んでおもしろい。
結構その人の経歴に関する話が長いので自分のようにコンピュータ史が好きな人にはいいかも。

ほかに、意外だったのはオブジェクト指向や、デザインパターンが嫌われていたこと。
C++はあの混沌仕様なので嫌われるのはわかるとして。
オブジェクト指向自体やデザインパターンもあまりい印象の人が多くなかった。
結局シンプルイズベストということか。
Cがシンプルなのはわかるけど関数型言語はどうなのだろう。

また、設計にUMLを使うという話も少なく、それよりは紙と鉛筆で設計するとか、
IDEもEclipse等の統合開発環境ではなくviとEmacsだったりと、(この辺は昔の人が多いからかな?)
ハッカーといえば常に新しいツールに飛びついていろいろ試してみるイメージ
だったけど、意外にそういう人ばかりではないのねと思った。
かくいう自分もEmacsないと生きていけないですけどね。

2013年1月19日土曜日

bashでSIGCHLDをハンドル

trapコマンドを使用するとシグナル受けて動作することができる。

#!/bin/bash
trap 'echo SIGINT trapped.' SIGINT
sleep 1000
スクリプトを実行後Ctrl+Cを入力するとechoが表示される

^CSIGINT trapped.

また、bashでは擬似シグナルという仕組みが実装されており、
bashの終了やコマンドのエラーをtrapすることができる。

#!/bin/bash

trap 'echo ScriptExit.' EXIT
echo "Hello"

スクリプトの最終行のHello実行後にEXITがtrapされている。

Hello
ScriptExit.

で、ここからが本題。
この仕組みを利用してSIGCHLD(子プロセスの終了)をハンドリングしたかったのだけど
意図したとおりに動かない。
こんな感じ。

#!/bin/bash

trap 'echo GetSIGCHLD.' SIGCHLD
echo "SCIRPT_END"

期待動作はSCIRPT_ENDの前にGetSIGCHLDが表示されて欲しいが、 実際の出力結果は以下のとおり

PID TTY          TIME CMD
20082 pts/3    00:00:00 bash
20486 pts/3    00:00:00 sample.script
20487 pts/3    00:00:00 ps
SCIRPT_END

SIGCHLDがtrapされていないように見える。
で、いろいろ調べてどうもbashには動作モードがあり
モニターモードに設定していないとSIGCHLDがtrapできないらしい。
それを修正すると。
#!/bin/bash

set -m  # monitor mode set
trap 'echo GetSIGCHLD.' SIGCHLD
ps
echo "SCIRPT_END"

PID TTY          TIME CMD
20082 pts/3    00:00:00 bash
20500 pts/3    00:00:00 sample.scrip
20501 pts/3    00:00:00 ps

GetSIGCHLD.

SCIRPT_END

期待通り!
今度bashのモードについて調べてみよう。