Scala入門 その3

Scalaスケーラブルプログラミング[第2版]をゆっくり読み進める。

とりあえず4~8章までは読んだ。
4~7章は特に面白いと思える箇所はなかった。
(for式は面白そうだが、詳細な解説は別章となっているのがなぁ。。。)

8章はScalaの関数の機能の簡単な解説。
Scalaは関数型プログラミングをサポートしている言語なので
無名関数、部分適用、クロージャといった機能がScalaでどのように
提供されているかといった説明になる。
他の関数型言語を触ったこともあるので目新しい感じはしなかった。
なのでそれ以外についてメモ。


8章ではScalaの関数の特殊な形式として以下の機能の説明があった。

  • 連続パラメータ(可変長引数)をとる関数
  • 名前付き引数
  • 関数パラメータのデフォルト値の設定方法


Scalaの関数パラメータは型の後に*(アスタリスク)をつけると
そのパラメータは*をつけた型のArray(=連続パラメータ(可変長引数))として関数に渡される。

def echo(args: String*) = { ... } // <- echo関数のargsは関数内ではArray[String]

echo("hello", "world")          // 引数の数は可変なのでこれもOK。
echo("hello", "world", "aaaa", "cccc")  // これもOK。

ただし、連続パラメータを受け取る関数にその型のArrayをパラメータとして渡そうとしてもコンパイルエラーになる。連続パラメータをとる関数にArrayを渡す場合、Arrayの後ろに_*(アンダーバーとアスタリスク)を設定する必要がある。

echo(Array("hello", "world")) // NG:これはコンパイルエラー

echo(Array("hello", "world"): _*) // OK


名前付き引数は関数実行時に対象の関数のパラメータに定義された順番とは異なる順番でパラメータを渡すこと。

def calc(x: Int, y: Int, z: Int) = { x + (y * z) }

// 通常の関数実行
// パラメータx, y, zは関数に渡された順番で束縛される
calc(1, 2, 3)

// 名前付き引数での関数実行
// パラメータに値を束縛する順番も変えられる
calc(z = 1, x = 2, y = 3) // 名前で指定されたパラメータの値を束縛
calc(2, y = 2, z = 3)     // 2はxに束縛。


Scalaでは関数パラメータにデフォルト値を指定できる。
デフォルト値は関数パラメータの型の後に[= デフォルト値]のように書く。
関数実行時に値が束縛されないパラメータはデフォルト値が使用されるようだ。

def calc(x: Int = 10, y: Int) = { x * y }  // xのデフォルト値は10

scala> calc(y = 10)
res17: Int = 100

ただし、上記のcalc関数実行は名前付き引数でyに値を束縛することを明示しないとコンパイルエラーになった。デフォルト値が設定されてない方に勝手に束縛とかはないっぽい。

scala> calc(10)
<console>:9: error: not enough arguments for method calc: (x: Int, y: Int)Int.
Unspecified value parameter y.
              calc(10)

と思ったら、パラメータの順番を変えずにyの方にデフォルト値を設定するようにした場合は名前付き引数を使用しなくてもxの方に値が束縛された。ふ~む...

scala> def calc(x: Int, y: Int = 10) = { x * y } // xではなくyのデフォルト値を指定
calc: (x: Int, y: Int)Int

scala> calc(10)  // 名前付き引数なし
res18: Int = 100


8章の最後でScalaでの末尾再帰について書かれていた。
Scalaの末尾再帰の最適化は色々制約があるようだ。
これは後で色々試してみようと思う。


とりあえず今日はここまで。

Scala入門 その2

Scalaスケーラブルプログラミング[第2版]をゆっくり読み進める。
第3章の途中まで読んだ。

面白いと思ったのは、以下の仕組み。

  • 括弧で囲んだ1つ以上の引数を変数に適用した場合、Scalaはその変数のapplyメソッド呼び出しにコードを書き換える。
  • 括弧で囲んだ1つ以上の引数を変数に代入した場合、Scalaはその変数のupdateメソッド呼び出しにコードを書き換える。

上記の仕組みによりコレクションの構築や更新は、直感的なコードで記述できるようになっているようだ。

例:Listの構築

val list = List(1, 2, 3) // List.apply(1, 2, 3)に書き換えられる

例:Arrayの更新

val array = Array("aa", "bb", "cc")
array(2) = "dd" // array.update(2, "dd")に書き換えられる

updateの方は、Rubyのaccessorの仕組みと似たような感じに見える。

Haskellでソケットプログラミング

Haskellでソケットプログラミングをやってみる。


お試しで書いた時刻を返すだけの簡単なサーバ。

-- DateServer.sh --
module Main where

import System.IO
import System.Time
import System.Environment (getArgs)
import Network

-- 接続してきたクライアントに時刻を返して終了
main :: IO ()
main = do
    args <- getArgs
    let
        port = fromIntegral $ (read (head args) :: Integer)

    withSocketsDo $ do 
        serverSocket <- listenOn (PortNumber port)
        (clientHandle, clientHost, clientPort) <- accept serverSocket
        nowTime <- getClockTime        

        hSetBuffering clientHandle NoBuffering
        hPutStrLn clientHandle $ show nowTime


PortNumber型のコンストラクタに渡す値は
fromIntegralで変換した値じゃないとなぜかうまくいかない。。。
何気にそこが一番はまった。


ビルドして実行。

C:\work\haskell\SockTest\DateServer>ghc DateServer.hs

C:\work\haskell\SockTest\DateServer>DateServer.exe 8001


WindowsXPのtelnetコマンドから接続。

C:\Documents and Settings\miya0001>telnet localhost 8001
Thu Feb 28 00:46:57 東京 (標準時) 2013


ホストとの接続が切断されました。


withSocketsDo関数のソースを見ると
引数の関数?の実行が終わったあとに
socketをクローズしてくれるようだ。


とりあえず今日はここまで。

Haskellで秒を日時に変換

仕事で秒を日時に変換して確認したいことが多いので
簡単なコマンドをHaskellで自作してみた。


System.TimeモジュールのTOD関数を第1引数に経過秒を指定すると
ClockTime型のデータが生成されるらしい。

-- SecToDate.sh --
module Main where

import System.Time
import System.Environment (getArgs, getProgName)

main :: IO ()
main = do
    args <- getArgs
    prog <- getProgName

    if (length args) < 1
    then
        putStrLn $ "usage: " ++ prog ++ " seconds."
    else
        putStrLn $ show $ TOD (read (head args) :: Integer) 0


Windows上でビルド。

C:\work\haskell\SecToDate>ghc SecToDate.hs
[1 of 1] Compiling Main             ( SecToDate.hs, SecToDate.o )
Linking SecToDate.exe ...


実行。

C:\work\haskell\SecToDate>SecToDate.exe 1000000000
Sun Sep  9 10:46:40 東京 (標準時) 2001

C:\work\haskell\SecToDate>SecToDate.exe 0
Thu Jan  1 09:00:00 東京 (標準時) 1970

C:\work\haskell\SecToDate>SecToDate.exe
usage: SecToDate.exe seconds.


Haskellでプラグラム書くのは楽しい上に
Windows上では単体動作可能なexeが生成できるのが
個人的にすごくうれしかったりする。


今までツール類はJavaRubyで作ってるんだけど
ランタイム環境が必要だったりで
実行可能な状態で配布する方法にはすごく悩んだりするんだよね。。。

Haskellのsshライブラリのインストール

Haskellの勉強を再開中。

 

cabal経由でHaskellsshパッケージをインストールしようとしてハマった部分をメモ。

環境はWindowsXP と Haskell Platform for Windows (http://www.haskell.org/platform/windows.html) 。

 

まずは

> cabal install ssh

 をコマンドプロンプトで実行。

依存関係にあるパッケージのダウンロード開始。

 

> Resolving dependencies...
> Configuring HsOpenSSL-0.10.3.3...
> cabal: Missing dependencies on foreign libraries:
> * Missing C libraries: eay32, ssl32
> This problem can usually be solved by installing the system packages that
> provide these libraries (you may need the "-dev" versions). If the libraries
> are already installed but in a non-standard location then you can use the
> flags --extra-include-dirs= and --extra-lib-dirs= to specify where they are.
> cabal: Error: some packages failed to install:
> HsOpenSSL-0.10.3.3 failed during the configure step. The exception was:
> ExitFailure 1
> ssh-0.2.10 depends on HsOpenSSL-0.10.3.3 which failed to install.

 

その後ビルドが実行されるのだけどOpenSSL関連のライブラリのビルドに失敗して結局インストール失敗。Missing C libraries: eay32, ssl32 って言われてるからOpenSSLをインストールすればよさげ。

 

Windows環境向けのOpenSSLのパッケージを探してWin32 OpenSSL(http://slproweb.com/products/Win32OpenSSL.html)のページへ。インストールするのは開発者向けっぽいWin32 OpenSSL v1.0.1c(Lightって付いてない奴)。

 

Win32 OpenSSL v1.0.1cをインストールしようとして、Visual C++ 2008 Redistributable?がインストールされてないとかエラーメッセージが出たのでMicrosoftのページ(http://www.microsoft.com/en-us/download/details.aspx?id=29)からMicrosoft Visual C++ 2008 Redistributable PackageをダウンロードしてOpenSSLより先にインストール。ここで日本語版(ダウンロードページのChange languageがJapanese)じゃなくて英語版(English)をインストールしないと、OpenSSLのインストール時のエラーメッセージが消えなかったのには1時間ちかくはまった。

 

OpenSSLのインストール時にライブラリの配置先(OpenSSLのインストール先のbinの配下 or システムディレクトリ配下)を聞かれる。システムディレクトリはあまり汚したくないので、bin配下を選択。

 

OpenSSLのインストールが終わったらコマンドプロンプトで再び

> cabal install ssh

を実行するもまた失敗。

 

あ、ヘッダやライブラリのパスをcabalに教えてあげないといけないのね。

> cabal install ssh --extra-lib-dirs=%OPENSSL% --extra-include-dirs=%OPENSSL%\include

(%OPENSSL%がOpenSSLのインストール先のディレクトリのパス。)

 

ビルド成功。

今日はここまで。

 

CentOSのyum updateでエラー

自宅のCentOSマシンでyum updateを実行すると
以下のようなエラーが発生してupdateに失敗するようになっていた。

$ sudo yum update
Loaded plugins: downloadonly, fastestmirror
Loading mirror speeds from cached hostfile
 * base: www.ftp.ne.jp
 * epel: ftp.yz.yamagata-u.ac.jp
 * extras: www.ftp.ne.jp
 * rpmforge: ftp-stud.fht-esslingen.de
 * updates: www.ftp.ne.jp
Setting up Update Process
Resolving Dependencies
--> Running transaction check
---> Package python.i386 0:2.4.3-44.el5_7.1 set to be updated
---> Package python-libs.i386 0:2.4.3-44.el5_7.1 set to be updated
http://mirror.centos.org/centos/5/cr/i386/repodata/filelists.sqlite.bz2: [Errno 14] HTTP Error 404: Not Found
Trying other mirror.
Error: failure: repodata/filelists.sqlite.bz2 from cr: [Errno 256] No more mirrors to try.
 You could try using --skip-broken to work around the problem
 You could try running: package-cleanup --problems
                        package-cleanup --dupes
                        rpm -Va --nofiles --nodigest


http://mirror.centos.org/centos/5/cr/i386/repodata/filelists.sqlite.bz2
を探しにいったが削除されているようでエラーになっているっぽい。


googleに聞いて色々調べた結果(ソースは失念)

$ sudo yum clean all
$ sudo yum update

でupdate出来た。


yum clean allを実行すると
yumのレポジトリ毎のファイルリスト(?)のキャッシュが
全てクリアされてリストが再構築されるので
無効なレポジトリは無視される用になるって感じかな。