SICPを読む会 1 問題1.3

読む会SICPを黙々と読む。

SICP Support for DrRacketのインストールを参考にDrRacketをインストールして実行環境を作成します。
DrRacketで言語宣言ペイン(上側のペイン)で、#lang planet neil/sicp と書いて実行しても調べたページとは違う挙動しましたが Schemeが実行出来るようなで練習問題を実施。

今日は問題1.3を実施。
今回は考え方としては3値のうち一番小さい値は何かという観点で手続きを定義します。
下記の一番最初のifの評価としては、『xはyよりも大きい、またはxはzよりも大きいのどちらもFalseになる = xは最も小さい』という周りくどい評価を実施しています。

> (define (square x) (* x x))
> (define (sum-of-squares x y)
    (+ (square x) (square y)))
> (define (f x y z)
    (if (or (>= x y) (>= x z)) 
        (if (or (>= y z) (>= y x)) 
            (sum-of-squares x y)
            (sum-of-squares z x)) 
        (sum-of-squares y z)))
> (f 3 4 5)
41
> (f 4 3 5)
41
> (f 4 5 3)
41

また、schemeではminという手続きが定義されており最小の値を求めることが出来るので、以下のように書き換えることが出来ます。

> (define (f x y z)
    (if (= (min x y z) x) (sum-of-squares y z)
        (if (= (min x y z) y) (sum-of-squares x z)
            (sum-of-squares x y))
        )
    )
> (f 3 4 5)
41
> (f 4 3 5)
41
> (f 5 4 3)
41

このような場合はifではなくcondを使ったほうが可読性が上がるので、condを使って書き換えます。

> (define (f x y z)
    (cond ((= (min x y z) x) (sum-of-squares y z))
          ((= (min x y z) y) (sum-of-squares x z))
          (else (sum-of-squares x y))))
> (f 3 4 5)
41
> (f 4 5 3)
41
> (f 5 3 4)
41

goのお勉強1

スコープ

変数や関数名で1文字目が小文字だとパッケージ内からのみ見える。大文字だとパッケージ外からのみ見える。

ポインタ

ポインタは、C言語と同様に利用できる。

package main

import "fmt"

func main() {
  a := 5
  var pa *int = &a // *intはint型のpointer
  fmt.Println(pa) // アドレスが表示される
  fmt.Println(*pa) // 値(5)が表示される

  *pa = 10
  fmt.Println(a) // 10が表示される
}

ただし、C言語と違いポインタは演算は出来ない。 c言語だと配列とかはポインタで表現され、以下のようにできるがgoでは出来ないことを確認する。 C言語では以下のように記述できる。

#include <stdio.h>

int main() {
    int  array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int* p = array;

    /* 配列の全要素の値を表示する */
    for (int i = 0; i < 10; ++i) {
        printf("*p = %d\n", *p);
        p = p + 1;  /* ポインタの値を 1 増やす */
    }

    return 0;
}

goで同じような処理をしようとしてみるとinvalid operationとコンパイルエラーになる。
スライスにしても同様にコンパイルエラーになる。

package main

import "fmt"

func main() {
  arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  var p *[10]int = &arr
  for i := 0; i < 10; i++ {
    fmt.Println(*p) // [1 2 3 4 5 6 7 8 9 10]が出力される
    // fmt.Println(p[i]) とすると配列のi番目の値を出力できる
    p = p + 1    // invalid operation: p + 1 (mismatched types *[10]int and int) となりコンパイルエラーになる。
  }
}

Omnibus InstallerでインストールしたChefのRubyでserverspecを実行する

chefのバージョンを確認する。

# chef-client -v
Chef: 12.0.3

Rubyのバージョンを確認する。

/opt/chef/embedded/bin/ruby -v
ruby 2.1.4p265 (2014-10-27 revision 48166) [x86_64-linux]

rubyなどはchef/embeddedに入っているので、serverspecのインストールを行う。

/opt/chef/embedded/bin/gem install serverspec

serverspec-initを実行する

# /opt/chef/embedded/bin/serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 2

 + spec/
 + spec/localhost/
 + spec/localhost/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec

serverspecを実行する

# cd spec/localhost
# /opt/chef/embedded/bin/rake

パーフェクトRuby写経 特異メソッドの作り方

特異メソッドとは、クラスに定義されたメソッドの他に、そのオブジェクト固有のメソッド持つことができ、それを特異メソッドといいます。

特異メソッドを定義するには、メソッドを定義したいオブジェクトをメソッド名の前に指定してメソッドを記述します。 また、特異メソッドの中でsuperを呼び出すと、クラスに定義されている同名のメソッドが呼び出されます。

class Foo
    def override_me
        p 'in Foo class'
    end

end

foo = Foo.new
bar = Foo.new

def bar.singleton_method
    p 'Called!'
end

def bar.override_me
    p 'in Foo class'
end

bar.singleton_method
bar.override_me

モジュールに定義されたメソッドを、オブジェクトの特異メソッドとして取り込む。 そのさいには、extendを用います。

module Sweet
    def self.lot
        %w(brownie apple-pie bavarois pudding).sample
    end

    def greet_to(name)
        p "Hello, #{name}. My name is #{self.class}"
    end
end

o = Object.new
o.extend Sweet

o.greet_to 'world'
#o.lot

p Sweet.lot

また、self.lotのようにモジュールのメソッド名の前にselfをつけて定義すると、モジュールの特異メソッドとなります。

また、モジュール関数は「privateなインスタンスメソッドであると同時に、モジュールの特異メソッドでもある」メソッドとの事なので、これも特異メソッドの一種のようです。

module MyFunctions
    def my_module_func
        p 'Called!'
    end
    module_function :my_module_func
end

一つのメソッドの場合は上記のように記述し、複数のメソッドを定義さする場合には下記のように記述するようです。

module MyFunctions
    module_function 

    def my_first_func
        p 'first!'
    end

    def my_second_func
        p 'second!'
    end

end

chefdashを試す

chefdash by sidebolt を試してみます。 chefdashはchef-clientのノードの登録や、environment毎やserver毎にcookbookを実行するWebUIを提供するWebアプリケーションです。

必要なものを事前にインストールしておきます。

sudo apt-get install git git-core
sudo apt-get install libssl-dev
sudo ./install.sh
sudo -i -u chefdash

あとはソースコードを取得し、chefdash by sidebolt のRedmeに書いてある手順で実行します。

注意点

chefdashユーザでknifeコマンドを実行できるようにしておく必要があるため、knife configure --initialを実行します。

knife bootstrap用の設定

chefdashでは、それぞれのchef-clientノードにknife bootstrapコマンドを利用してchef-clientのインストール・設定を行うことが可能です。 chefdashで利用しているknife bootstrapのオプションは--sudoです。 特に何も設定しないで、実行すると下記のようなエラーがでます。

f:id:clavier:20131203225559p:plain

このようなエラーが出ますので、事前にrootにパスワードなしでログインできるようになっている必要があります。
chefdashサーバから、chef-clientをインストールするノードにパスワード無しでログインできるようにする必要があります。 chefdashサーバからroot ユーザの環境にSSH の公開鍵と秘密鍵をコピーします。

$ scp .ssh/id_rsa root@chef-clientノードのIPアドレス:/root/.ssh
$ scp .ssh/id_rsa.pub  root@chef-clientノードのIPアドレス:/root/.ssh

chef-clientをインストールされる側のノードでは以下の設定をします。

$ sudo chown root. /root/.ssh/id_rsa
$ sudo chown root. /root/.ssh/id_rsa.pub
$ sudo cat /root/.ssh/id_rsa.pub | sudo tee -a /root/.ssh/authorized_keys

これで、chefdashがknife bootstrapを正常に実行出来るようになります。

f:id:clavier:20131203231715p:plain

chef-clientの実行

chef-clientを実行する前に、ノードでcookbookを実行するようにrun-listを登録しておく必要があります。 chefdashが実際にchef-clientコマンドを実行してCookBookを実行するときは、sshでchef-clientノードに入ってchef-clientコマンドを実行しています。 chefdashが実行しているコマンドは、下記のコマンドです

ssh -o StrictHostKeyChecking=no chef-clientのIPアドレス sudo chef-client

上記のコマンドがパスワードの問い合わせなど発生せずに実行出来る必要があります。 そのためにchefdashユーザがsshでログインできるように各マシンに SSH の公開鍵と秘密鍵をコピーしておく必要があります。

$ scp .ssh/id_rsa root@chef-clientノードのIPアドレス:/home/ubuntu/.ssh
$ scp .ssh/id_rsa.pub root@chef-clientノードのIPアドレス:/home/ubuntu/.ssh
$ sudo chown ubuntu. /home/ubuntu/.ssh/id_rsa
$ sudo chown ubuntu. /home/ubuntu/.ssh/id_rsa.pub
$ sudo cat /home/ubuntu/.ssh/id_rsa.pub | sudo tee -a /home/ubuntu/.ssh/authorized_keys

また、chef-clientノードで、chef-clientコマンドをsudo実行できるように、visudoなどで設定しておく必要があります。 cookbookの実行が成功すると、下記のように表示されます。

f:id:clavier:20131203090545p:plain

雑感

最初はchef-zeroと組み合わせて利用できたら面白いかと思って触ってみが、認証が必要ということであえなく挫折。
chef-serverと組み合わせて確認することになりました。
chef-clientやkinife bootstrapの実行ログ(標準出力に出力される内容)をwebsocketで画面に描画したり、そのあたりの実行結果もwebsocketでやりとりをするという仕組みは面白いと感じました。

複数のサーバに対してcookbookを頻繁に実行するような環境を持っているなら構築する価値はあるかもという感じでした。反面構築時に1回だけ実施するような利用の仕方だと、それほど魅力的はないかと思いました。

f:id:clavier:20131206002329p:plain