HaskellとGoとRustを調べた

研究室で書いているプログラムはC言語だけど、文字を筆頭に配列の扱いがめんどうでなかなか書く気がおきない。それに、なにか新しいコンパイル言語を勉強してみたいのでHaskellとRUSTとGoを調べてみた。配列だけじゃなくてメモリ管理、並列化が簡単にかける言語がいいな。

Go

GoはCの代替のためにGoogleが開発した言語だ。ポインタを排除したり、GCを導入したり、オブジェクト指向だったりと、CからJavaC#への移り変わりと似ている。Goではクラスや継承という概念はないがインターフェイスというものがあるらしい。

Goのインターフェイスは、C++でいえば、純粋仮想関数に似ているという。データメンバがなく、すべての関数が仮想関数であるようなクラスだ。Goでは、あるインターフェイスが定義するメソッドをすべて実装した型は、そのインターフェイスを実装しているものと見なされる。こうして個々のメソッドという実装をクラスという概念でまとめるよりも、より柔軟な型とメソッドの対応付けが可能になるのだという。[1]

ダックタイピングでプロトタイプ継承みたいな感じかな? オブジェクティブにかけたりGCしてくれるのはとても嬉しいけど、計算プログラムでのその恩恵とCから書き換えることの労力を比べるとCのままでいいんじゃないって思ってしまった。でも、Goルーチンは気になる。

参考文献

  1. グーグル、C/C++に代わる新言語「Go」をOSSで公開 - @IT
  2. 【翻訳】Go言語がダメな理由 | POSTD
  3. 動的言語だけやってた僕が、38日間Go言語を書いて学んだこと - Qiita

Rust

RustはMozillaが開発している言語。まだ開発途中で遅いらしい[2]。遅いのは残念ながらダメだ。文法はC++に酷似している。並列計算に力を入れているそうなのでそこのところをくわしく調べてみたい。

参考文献

  1. 今さらRust試してみた(ついでにGo言語と少し比較) - Blank File
  2. Rust触ってみたらかなり良かった話 | ブログ :: Web notes.log

Haskell

Haskellは1990年から開発が始まった純粋関数型プログラミング言語。Wiki読んで意味が分かったのは型推論と参照透過性と遅延評価ぐらい。モナドとかよくわからない。純粋関数型ということで関数を実行してもモナドを使わなければ副作用なしで返ってくる。数値計算プログラムは書きやすそう。手続き型プログラミング言語とはかなり書き方が違うみたいでおもしろそう。並列かとかは簡単にできるのかしら。返り値の使用時に値を評価する遅延評価ってのがどれくらい自分がする計算に効果を発揮するのか計測してみたい。ほかの言語とは毛色の違う言語なので勉強するのはおもしろそうだ。

参考文献

  1. 本物のプログラマはHaskellを使う - 第1回 関数型プログラミングの世界へようこそ:ITpro
  2. Haskell - Mac OSX 10.9 (marvericks) に GHC 7.8.2 をインストールする方法 - Qiita
  3. タプル - Wikipedia
  4. Haskell - Wikipedia
  5. 参照透過性 - Wikipedia

Dropbox API メモ

Dropbox API メモ

エラー

ステータス 説明
400 入力パラメータが不正。
401 tokenが不正または期限切れ。もう一度認証の必要がある
403 OAuthリクエストが不正。開発側の責任
404 ファイルまたはフォルダーが指定の場所に存在しない
405 リクエストメソッドが不正(GETかPOSTのみ)
429 リクエスト回数過多(アプリ・ユーザーごとに存在する)
503 一時的なサーバーエラー
507 ユーザーの容量不足
5xx サーバーエラー

認証

1. 認証コードを発行する方法

flow = DropboxOAuth2FlowNoRedirect.new(APP_KEY, APP_SECRET)
authorize_url = flow.start()

# authorize_urlにアクセスして連携を許可する。その後出てくるコードを入力させる

code = gets.strip
access_token, uer_id = flow.finish(code)

2. 認証後リダイレクトする方法

flow = DropboxOAuth2FlowNoRedirect.new(APP_KEY, APP_SECRET, REDIRECT_URI, SESSION, CSRF_TOKEN_SESSION_KEY)
authorize_url = flow.start()

# redirect_uri先でquery_paramsにはDropboxから来たrequestのpqramsを入れる
access_token, user_id = flow.finish(query_params)

参考にしたもの

SQLインジェクション

ActiveRecordにおけるSQLインジェクション

ActiveRecordを用いた以下のコードはプロジェクトテーブルからプロジェクト名の一致するレコードをSELLECTするクエリであるが、ある危険を孕んでいる。

Project.where("name = '#{params[:name]}'")

このコードはSQL Queryとしては以下のように解釈される。

SELECT * FROM projects WHERE name = '#{params[:name]}'

この時#{params[:name]}に以下の検索語が挿入されたらどうなるだろうか。

' OR 1 --

--はそれ以下の文字をコメントとして解釈することを示している。また、OR 1とすることで常にtrueを返すようにしてしまっている。つまり以下のようになるSQL QueryはProjectテーブルのレコードを全件取得してしまうのだ。

SELECT * FROM projects WHERE name = '' OR 1 --'

このように、開発者の意図しないようなSQL Queryを発行し、認証をすり抜けたりSQL内のデータ改ざんや身勝手な閲覧をすることをSQLインジェクションという。

対抗策

このような不正に対してRoR特殊文字をフィルターすることで対応している。フィルターするためには以下のように?を用いる。

Project.where("name = ?", params[:name])

また、以下のようにhashでわたすことも可能だ。

Project.where(name: params[:name])

参考にしたもの

Ruby on Rails Security Guide — Ruby on Rails Guides

MacBookPro Late 2013, MacBookAir Late 2010 の解体・清掃をする

最近、MacBookProがただ起動しているだけでファンをまわすようになった(これじゃ艦これがままならない!)。購入してから8ヶ月近く経っている訳だし、ファン周りの掃除をしてもいい頃合いかなと思って早速解体掃除してみた。

保証は?

解体の前に一つ確認する。解体することで製品保証がどうなるか、だ。Appleの保証規定(Apple - Legal)に保証が適用されない条件が書いてある。

本保証は、以下のいかなる場合においても適用がありません:(a) バッテリー等の消耗品の場合、ただし損害が材質上または製造上の瑕疵により生じた場合はこの限りではありません、(b) 表面的な損傷の場合、なおこれには、かすり傷、へこみ、ポートのプラスチックの欠損を含むものとしますが、これに限りません、(c)別の製品とともに使用することによって生じる損害の場合、(d)事故、乱用、誤使用、液体接触、火事、地震または他の外的原因による損害の場合、(e) Appleの発行するガイドラインに定める以外の方法でApple製品を作動させたことにより生じる損害の場合、(f) Appleの担当者またはApple正規サービスプロバイダ(以下「AASP」といいます)以外の者が履行したサービス(アップグレードや拡張を含みます)によって生じる損害の場合、(g) Appleの書面による許可なく機能性もしくは性能を変更するためにApple製品が改造された場合、(h)自然損耗やその他Apple製品の経年劣化による瑕疵の場合、(i)Apple製品からシリアル番号が剥がされたり汚損されたりしている場合。

改造は保証適用外みたいだけど、解体することについては触れていない。内部を傷つけないようにすれば解体しても大丈夫だよね??

使用した工具

調べてみると解体にはペンタローブドライバとT5トルクスドライバが必要って記事が出てくるけど、とりあえず背面カバーをあけるだけならペンタローブドライバだけでいいっぽい。使用されているネジの軸径は1.2mmだけど、同じ径でも入らなかったドライバもあったので購入時にちゃんと確認した方がいい。僕が購入したのはCellphone Precision Screwdriver /PENGGONG No.8024千石電商で売ってた。

ほこりを吹き飛ばすためのエアダスター工具のホーザン■Z–282/283 エアダスターを買った。ついでにSunhayatoのクリーニングクロスも購入。

MacBookAirの掃除

興奮しててMacBookProの掃除の様子は撮り忘れたぜ。

まず開ける。

f:id:kazukitash:20160220155208j:plain

f:id:kazukitash:20160220155218j:plain

きたねー。

f:id:kazukitash:20160220155233j:plain

f:id:kazukitash:20160220155238j:plain

f:id:kazukitash:20160220155242j:plain

エアダスターとクリーニングクロスを使って掃除! めっちゃきれいになる。

f:id:kazukitash:20160220155304j:plain

f:id:kazukitash:20160220155320j:plain

f:id:kazukitash:20160220155326j:plain

最後に起動を確認して終了。

f:id:kazukitash:20160220155341j:plain

解体・掃除してみて

  • わりと簡単に解体できる。背面カバーを外すときはピン止めされているので少し強め力を入れる
  • ファンが一番汚い
  • なんか掃除後にDesktopの背景が初期化されてる

brew で python3 を入れる

とりあえず brew update & upgrade する

$ brew update
$ brew upgrade

pyenvでpythonをバージョン別に導入する

$ brew install pyenv

.zshrcに以下を追加する。

eval "$(pyenv init -)"
export PYENV_ROOT=/usr/local/opt/pyenv

source .zshrc をした後にpyenvでpythonを導入する。pyenvで導入できるバージョンは

$ pyenv install -l

で確認できる。以下のコマンドでinstallできる。

$ pyenv install <入れたいpythonのバージョン>

pyenv-virtualenvbrew でいれればさらにsandbox化もできるらしい。

追記

numpyとscipyを入れる。3.4.0はdefaultで pip が入っているので以下のコマンドでOK。ただしfortranのscipyはfortranコンパイルが必要なのであらかじめbrew install gfortranでもしていれておく。

$ pip install numpy
$ pip install scipy

Rakefileで快適コンパイル生活

f:id:kazukitash:20160220154420p:plain

コンパイルしたいときはたった四文字打つだけ。

$ rake

Rubyをよく使うのでC言語コンパイル処理をRakefileに書いた。

require 'rake/clean'
CLEAN.include("**/*.o")
CLOBBER.include("*.out")

CC = "gcc"
GFLAGS = "-g -Wall"
OPT = "-O3"
INCDIR  = "lib"
SRCS = FileList["**/*.c"]
OBJS = SRCS.ext('.o')
PROGRAMS_DIR = FileList.new("**/*.c").exclude("lib/*").ext('.out')
PROGRAMS = PROGRAMS_DIR.map{|p| File.basename(p)}
DATA_DIR = "data"
directory DATA_DIR

task default: PROGRAMS

desc "初期化"
task init: [DATA_DIR, :reset]

desc "中間ファイルを削除してからビルド"
task refresh: [:clean, :default]

desc "実行ファイルを削除してからビルド"
task reset: [:clobber, :default]

desc "依存ファイルのビルド"
task dist: OBJS

def dep_obj program
  headers = [program.ext('.o')]
  open(program.ext('.c')){ |f| headers.push "/#{$1.ext('.o')}" if $_ =~ /#include\s+"(.+)"/ while f.gets }
  OBJS.select{|obj| obj =~ Regexp.union(headers) }
end

PROGRAMS_DIR.each do |program|
  desc "#{File.basename(program)}をビルド"
  file File.basename(program) => dep_obj(program) do |t|
    sh "#{CC} #{OPT} #{GFLAGS} -o #{t.name} #{t.prerequisites.join(' ')}"
  end
end

rule '.o' => '.c' do |t|
  sh "#{CC} #{OPT} #{GFLAGS} -c #{t.source} -o #{t.name} -I#{INCDIR}"
end

固定タスクは initrefreshreset で文字通りのことしてる。rake/cleanを require することでcleanとclobberというタスクが追加される。

CLEAN.include("**/*.o")
CLOBBER.include("*.out")

このようにして削除したいファイルをincludeしておくとタスクを実行したときに消してくれる。いちおうcleanは中間ファイル、clobberをclean + 実行ファイルを消すためのものらしいけど、違いはいまいち分からない。

PROGRAMS_DIR = FileList.new("**/*.c").exclude("lib/*").ext('.out')
PROGRAMS = PROGRAMS_DIR.map{|p| File.basename(p)}

defaultタスクは配下のすべてのプログラムをコンパイルしている。libを除いたフォルダの中のC言語をそれぞれ実行ファイル形式(.out)にコンパイルしている。FileListでCファイルのPATHを求めて実行ファイル名を作成している。ext()はFileListの関数で拡張子を任意の文字列に変換できる。

DATA_DIR = "data"
directory DATA_DIR

...ruby
task init: [DATA_DIR, :reset]

directoryはFileUtilsの関数だけど[1]タスクのように指定することでディレクトリを作成してくれる。

PROGRAMS_DIR.each do |program|
  desc "#{File.basename(program)}をビルド"
  file File.basename(program) => dep_obj(program) do |t|
    sh "#{CC} #{OPT} #{GFLAGS} -o #{t.name} #{t.prerequisites.join(' ')}"
  end
end

動的に実行ファイルのコンパイルタスクを生成している。

def dep_obj program
  headers = [program.ext('.o')]
  open(program.ext('.c')){ |f| headers.push "/#{$1.ext('.o')}" if $_ =~ /#include\s+"(.+)"/ while f.gets }
  OBJS.select{|obj| obj =~ Regexp.union(headers) }
end

Cファイルを開きincludeされているものを読んでいる。 if $ =~ /#include\s+"(.+)"/ は$が暗黙の変数なので if /#include\s+"(.+)"/ でもOK。でもwarningが出るので極力やめよう。

rule '.o' => '.c' do |t|
  sh "#{CC} #{OPT} #{GFLAGS} -c #{t.source} -o #{t.name} -I#{INCDIR}"
end

rakeのruleでOファイルが同名のCファイルから作成できることを書いた。


  1. rakefileはFileUtilsを自動でrequireしてくれている。  ↩

MacBookAir'11 OSX 10.9 Mavericks でRailsアプリケーションサーバー構築

1. nginxはhomebrewでインストール。

$ brew install nginx

設定は /usr/local/etc/nginx/nginx.conf に記述する。Mac + unicorn + nginx + Rails  - Giworldを参考した。

worker_processes  2;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;

    keepalive_timeout  65;

    upstream unicorn.rails_app {
        # 自分のrailsアプリの場所
        server unix:/Users/kazukitash/ruby_src/jitaku/tmp/sockets/unicorn.sock fail_timeout=0;
    }

    server {
        listen       8080;
        server_name  jitaku-app.com;

        charset utf-8;

        location / {
            alias /Users/kazukitash/ruby_src/jitaku/public;
            index  index.html index.htm index;

            try_files $uri/index.html $uri.html $uri @unicorn_rails_app;
        }

        error_page   500 502 503 504  /50x.html;
        # location = /50x.html {
        #     root   html;
        # }
        location @unicorn_rails_app {
            proxy_redirect off;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-Host $host;
            proxy_set_header X-Forwarded-Server $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://unicorn.rails_app;
        }
    }
}

nginxのコマンドは次の通り。

  • 起動 nginx
  • 終了 nginx -s stop
  • 再起動 nginx -s reload
  • 設定の検証 nginx -t

2. 次にunicornをgemで入れる。

$ gem install unicorn

[RAILS_DIR]/config/unicorn.rbを作成し、編集。これもMac + unicorn + nginx + Rails  - Giworldを参考した。

#ワーカー数
worker_processes 2

#ソケット経由で通信する
listen File.expand_path('tmp/sockets/unicorn.sock', ENV['RAILS_ROOT'])

# ログ
stderr_path File.expand_path('log/unicorn.log',  ENV['RAILS_ROOT'])
stdout_path File.expand_path('log/unicorn.log',  ENV['RAILS_ROOT'])

# ダウンタイムをなくす
preload_app true

before_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!

  old_pid = "#{server.config[:pid]}.oldbin"
  unless old_pid == server.pid
    begin
      # StGTTOU だと worker_processes が多いときおかしい気がする
      Process.kill :QUIT, File.read(old_pid).to_i
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end
end

after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end

unicornに必要なgemをGemfileに書く。

group :production do
  gem 'unicorn'
  gem 'execjs'
  gem 'therubyracer'
end

bundle install をしたら以下のコマンドでunicornが起動する。

unicorn_rails -c config/unicorn.rb -E production

デーモンにしたかったら以下のようにする。

unicorn_rails -c config/unicorn.rb -E production -D

ちなみにデーモンにしたときは kill #{pid} で終了させる。pidはpsで見れる。

ps -ef | grep unicorn_rails

localhost:8080でrailsアプリが表示されたら成功。

参考にしたサイト

Mac + unicorn + nginx + Rails  - Giworld nginxにreloadシグナルを送っても設定を再読み込みしてくれなかったりなかなかKILLされなかったり - There’s an echo in my head nginx入門