2012年2月7日火曜日

Scalaの関数とメソッドの宣言方法のまとめ

ちなみに、Scalaでメソッドと呼ばれるものは、defキーワードで宣言されたもののことです。
サンプルとして、2つの引数をとり、それらを掛け合わせたものを返す関数とメソッドを作ってみます。

もっともオーソドックスな関数

まず関数の宣言方法は以下です。

scala> val multiply:(Int, Int) => Int = (x:Int, y:Int) => { x * y } multiply: (Int, Int) => Int = <function2> # 実行 scala> multiply(5,10) res8: Int = 50

(Int, Int) => Int
この部分が関数の型の宣言です。
第1引数にInt、第2引数にIntをとり、最終的にIntを返す関数という宣言になります。

(x:Int, y:Int) => { x * y }
この部分が実際の関数の宣言です。
なお、=>{...}の部分は、通常コードブロックと呼ばれます。
xとyという変数で値を受け取って、x * yを実行します。
Scalaでは最後に評価された式が戻り値となりますので、x*yの実行結果(Intどうしの掛け算なので当然Int型)が帰ります。

なお、実はこの関数は無名関数です。
multiplyというのはあくまで変数名であり、この関数自体には名前がありません。

「(Int, Int) => Int 」という型の変数に、イコール(=)を使って、関数「 (x:Int, y:Int) => { x * y }」を代入しているということになります。


もっともオーソドックスなメソッド

次はメソッドで同じものを作ってみます。

scala> def multiply(x:Int, y:Int):Int = { x * y } multiply: (x: Int, y: Int)Int # 実行 scala> multiply(5,10) res9: Int = 50

def multiply(x:Int, y:Int):Int
この部分がメソッドの宣言です。
第1引数にInt、第2引数にIntをとり、最終的にIntを返すメソッドという宣言になります。

= { x * y }
この部分が実際のメソッド本体ですです。
xとyという変数で値を受け取って、x * yを実行します。


関数の省略記法

幾つかパターンはありますがとりあえず以下の2パターンが良く使われると思います。

# コードブロックの中括弧{}を省略 scala> val multiply:(Int, Int) => Int = (x:Int, y:Int) => x * y multiply: (Int, Int) => Int = <function2> # 思い切って関数の型を省略 scala> val multiply = (x:Int, y:Int) => x * y multiply: (Int, Int) => Int = <function2> # 引数なしの関数はこれ scala> val multiply:() => Int = () => 10 * 5 multiply: () => Int = <function0> # 関数の型を省略 scala> val multiply = () => 10 * 5 multiply: () => Int = <function0>


メソッドの省略記法

結構パターンがあります。この省略記法を覚えるのには苦労させられます。

# コードブロックの中括弧{}を省略 scala> def multiply(x:Int, y:Int):Int = x * y multiply: (x: Int, y: Int)Int # 戻り値の型を省略 scala> def multiply(x:Int, y:Int) = x * y multiply: (x: Int, y: Int)Int # 引数も戻り値の型も中括弧も全て省略。クラスのゲッターなどに使える scala> def getName = "Yamada Tarou" getName: java.lang.String # これでも同じ scala> def getName() = "Yamada Tarou" getName: ()java.lang.String # 戻り値の型と中括弧を省略したパターン。クラスのセッターなどに使える scala> def setName(newName:String) = name = newName setName: (newName: String)Unit # 戻り値が無い(Unit)の場合は、コードブロックの前のイコールを書かなくても良い scala> def myPrint { println("Hello World") } myPrint: Unit # もちろん書いても良い scala> def myPrint = { println("Hello World") } myPrint: Unit # これも同じ scala> def myPrint:Unit = { println("Hello World") } myPrint: Unit # ただしこれはNG scala> def myPrint:Unit { println("Hello World") }
まだまだパターンはありますが、とりあえずこれでメソッドの省略記法のメジャーどころはつぶせたかなと思います。


関数の宣言に関する注意

ちゃんと関数として宣言せずに、とりあえずコードブロックで囲めばいいんでしょ?というノリでコーディングすると意図しない動作になってしまいます。

# 以下のような宣言をすると、myPrintにはUnitが入ってしまう scala> val myPrint = { println("Hello World") } Hello World myPrint: Unit = () # なので、何回myPrintを実行しても何も起こらない scala> myPrint() # 意図した関数として宣言するには以下のようにする scala> val myPrint:()=>Unit = () => { println("Hello World") } myPrint: () => Unit = <function0> scala> myPrint() Hello World scala> myPrint() Hello World
また、関数はオブジェクトなので、別の変数に格納することもできます。

# まずは関数定義 scala> val myPrint = () => {println("Hello World")} myPrint: () => Unit = <function0> # 括弧無しで呼び出すと関数本体が返される scala> myPrint res1: () => Unit = <function0> # 別の変数に代入 scala> val myPrint2 = myPrint myPrint2: () => Unit = <function0> scala> myPrint2() Hello World


メソッドの宣言に関する注意

Scalaでは、メソッドに戻り値があっても戻り値の型は省略できますが、メソッド宣言とコードブロックの間のイコールは省略できません。
厳密に言うと省略は出来るのですが、イコールを書略してしまうと値を返してくれないメソッドになってしまいます。

# コードブロックで文字列が評価されてStringが返りそうだけど・・・ scala> def getString { "Helo World" } getString: Unit # 実行すると何も帰ってきません scala> getString
これは、イコールを省略するとメソッドが自動的にUnitになってしまうためです。
イコールを付ければ期待した動作になります。

scala> def getString = { "Helo World" } getString: java.lang.String scala> getString res24: java.lang.String = Helo World
次に、関数の場合は括弧()なしで実行すると関数自体が返されましたが、メソッドは動作が異なります。
メソッドの宣言で、引数リストの括弧()を省略した場合には呼び出し方法が制限されます。

scala> def myPrint { println("Hello World") } myPrint: Unit # こちらはOK scala> myPrint Hello World # 括弧を付けるとエラー・・・ scala> myPrint() <console>:9: error: Unit does not take parameters myPrint() ^
引数なしであっても、引数リストを省略せずに空の括弧()を付けておけばどちらでも実行できます。

scala> def myPrint() { println("Hello World") } myPrint: ()Unit scala> myPrint() Hello World scala> myPrint Hello World

2012年2月2日木曜日

bzr+ssh://で外部のBazaarリポジトリにアクセスする方法(Linux)


以下のコマンドでOK

bzr log bzr+ssh://{接続先ユーザ名}@{接続先ホスト名}:{接続先のSSHのポート}{接続先の共有リポジトリへのフルパス}

なお、接続先のサーバにはSSHですでに接続できていることが前提。
接続先のBazaarに特別な設定などは特に必要ありません。

接続先の情報が以下の場合、

・接続先:example.com
・接続先ユーザ名:hoge
・接続先のSSHのポート:1234
・接続先の共有リポジトリの場所:/home/hoge/myrepo

こうなります。

bzr log bzr+ssh://hoge@example.com:1234/home/hoge/myrepo

Linux系なら「~/.bazaar/authentication.conf」にポートとかの設定を書いておけるよ、と公式に書いているの、なぜか設定が反映されず。
自分の環境では、接続先のSSHのポートは22番以外なので、正しいポートをauthentication.confに書いていたのになぜか22番でアクセスしてタイムアウトしていました。
ということで、直接URLにポート番号を指定することで回避できました。

このあたりの情報が公式以外にあまりないんですけどBazaarユーザって少ないんですかね?