mike、mikeなるままに…

プログラムに関してぬるま湯のような記事を書きます

プログラマーがGitHubでブログを公開するべき理由

こんにちわ。みけです。

早いもので僕がブログをこちらに移動してから約1ヶ月経ちました。

こちらのブログに移動してよかったと思う理由を挙げてみたいと思います。

(1)小さく記事を書いていくことができる

ブログに自分の考えなどをまとめるのには、

それなりに時間が必要であったり、

集中力が必要であったりします。

これまでのブログサービスでも、

一応下書きという形で保存することは出来ましたが、

どうしても大きなものになってしまったりする場合、

こまめに保存できなかったりすると痛いことが多々あります。

GitHubでブログを書いている場合は、

こまめに変更が管理できるので、

ブログのように長いものを書くときに、

非常に便利です。

逆に間違えたなと思うところなどは、

消せますし、

消したところを間違えたという場合は復帰させることもできます。

というわけで、僕のようにダラダラ記事を書く人でも、

最終的に記事としてまとめられるので、

Gitでバージョン管理しながらブログを書くのは非常に便利であると思います。

結論

Gitでバージョン管理しながら書くブログは書きやすいです。

え、理由、そんなのこれしかないですお…(´・ω・`)

Windows(7)環境でgvmをインストールする

こんにちわ、みけです。

Windowsでのgroovyの環境構築といえば、

zipを落としてきて、解凍して、パスを通してといった作業をしなければならないのですが、

最近それが結構面倒くさいなと感じて、

Windowsから疎遠になっていました。

gvmをインストールすればいいんですが、

gvmのインストールコマンド

1
curl -s get.gvmtool.net | bash

はい、Windowsではできませんね。

cygwin

WindowsでGNUな環境を構築するといえば、cygwinですが、

僕は自分が認める(他人はどうだかよく知らん)情弱なので、

cygwin環境の構築に挫折した人です。

で、cygwinでgvmインストールできるよと言われても、

その元になるcygwinが構築できないので、

Windowsでgvm使えないじゃんと思っていました。

mingw

Windowsにはいっているmsysgitをいじっていて、

mingwなら構築できそうだと気づいたので、

やってみたら意外とできたので、

その作業メモを書いておきます。

ちなみに、参考にしたページはこちらです。

(1)Chocolateyのインストール

Windowsのパッケージ管理ツールです。

http://chocolatey.org/に書いてある

スクリプトをコピペしてインストールします。

(2)mingwのインストール

次のコマンドでインストール出来ます。

1
C:\> cinst mingw

なお、インストールの際に

  • C++ Compiler
  • MSYS Basic System
  • MinGW Developer Toolkit

も選択しておいたほうがいいのかなと思って、選択しています。 (よくわかってない…)

(3)msys-unzipのインストール

ここからはmingwでの作業になります。

なお、先ほどのリンクでも紹介されているConEmuで、Taskの設定で

1
C:¥MinGW¥msys¥1.0¥bin¥bash.exe --login -i

で、mingwを起動できるようにして、

そのコンソールから使っています。

1
mingw-get install msys-unzip

これでインストール出来ます。

(4)gvmのインストール

ここまでくれば

1
curl -s get.gvmtool.net | bash

を実行することができます。

これで、gvmでらくらくgroovyを扱えますね。

AndroidがJavaでない理由 - 1

こんにちは。

みけです。

また仰々しいタイトルですみません。

Androidのsdkが5月16日近辺にrevision22に更新されたようです

リリースノートを一部抜粋、翻訳(意訳)するとこんな感じです。

  • 既存のplatfomr-toolsを元にSDKの構造を変更して、新しいビルドツール等を追加しました。この変更により、ビルドツールのバージョンとIDEのバージョンを切り離すことが可能になります。その結果、IDEの更新なしにツールの更新が可能になりました。

さて、僕はAndroidに関しては情弱なので、この変更が意味するところをあまり気にせず、

本題である「JavaがAndroidでない理由」ことの実例コードを書こうとして

サンプルプロジェクトを作って、実行しようとした時にそれは起こりました。

android-apt-compiler: Cannot run program “/Users/name/Android/sdk/android-sdk-macosx/platform-tools/aapt”: error=2, No such file or directory

うん?コンパイル用のツールが見つからない?

というわけで、件のディレクトリーに移動、ファイル探してみると…

1
2
3
4
5
6
7
8
9
10
:~ mike$ cd ~/Android/sdk/android-sdk-macosx/platform-tools/
:platform-tools mike$ ll
total 3664
drwxr-xr-x   7 mike  staff   238B  5 31 19:05 .
drwxr-x---  14 mike  staff   476B  5 31 18:31 ..
-rw-r--r--   1 mike  staff   396K  5 31 18:30 NOTICE.txt
-rwxr-xr-x   1 mike  staff   1.2M  5 31 18:30 adb
drwxr-xr-x   3 mike  staff   102B  5 31 18:30 api
-rwxr-xr-x   1 mike  staff   185K  5 31 18:30 fastboot
-rw-r--r--   1 mike  staff    16K  5 31 18:30 source.properties

うむ、確かにそんなものはない…

で、いろいろとディレクトリーを漁ってみると

1
2
3
4
5
6
7
8
9
10
11
12
13
14
:~ mike$ cd ~/Android/sdk/android-sdk-macosx/build-tools/17.0.0/
:17.0.0 mike$ ll
total 61624
drwxr-xr-x  11 mike  staff   374B  5 31 18:30 .
drwxr-xr-x   3 mike  staff   102B  5 31 18:30 ..
-rw-r--r--   1 mike  staff    11K  5 31 18:30 NOTICE.txt
-rwxr-xr-x   1 mike  staff   1.2M  5 31 18:30 aapt
-rwxr-xr-x   1 mike  staff   273K  5 31 18:30 aidl
-rwxr-xr-x   1 mike  staff   141K  5 31 18:30 dexdump
-rwxr-xr-x   1 mike  staff   2.5K  5 31 18:30 dx
drwxr-xr-x   3 mike  staff   102B  5 31 18:30 lib
-rwxr-xr-x   1 mike  staff    28M  5 31 18:30 llvm-rs-cc
drwxr-xr-x   4 mike  staff   136B  5 31 18:30 renderscript
-rw-r--r--   1 mike  staff    16K  5 31 18:30 source.properties

お、あった。

…あったはいいよ。

でも、このディレクトリーは何?

17.0.0

何、このディレクトリー。

数字ディレクトリー。

どこのSIerの作業ですか?

え、じゃあ、君は新しいAPIバージョン(例えば18)が出たら、

18.0.0ディレクトリーでも作ってくれるんです?

これ、ビルドスクリプトとか、ビルド関連のちょっとした作業とか

ぶっ壊れるよね…え、ぶっ壊れない?なら、いいんだけど、

IntelliJ IDEA様を欺いているよね…

これじゃ

Androidってwrite once, run once upon a timeってことだよね

Javaの最も貫徹したコンセプトである

write once run anywhere

を破っているよね。

というわけで、結論

AndroidはJavaではなかった、決して

p.s.

最初この問題にあたった時にStack Overflowを調べました。

すでに、先駆者がいました。

Android Hello-World compile error: Intellij cannot find aapt

で、解決方法のアドバイスとして

  • JetBrains様がIDEをアップデートしてくれるのを待つしかないんじゃない?
  • シンボリックリンクを貼ればいいんじゃない
  • ツール類をplatform-toolsにコピペすればいいんじゃない

と有りました。

当面の解決策としては2番目が採択されそうですが、

1
2
3
:17.0.0 mike$ ln -s aapt ../../platform-tools/aapt
:17.0.0 mike$ ln -s aidl ../../platform-tools/aidl
:17.0.0 mike$ ln -s lib ../../platform-tools/lib

をやった後ですが、

はい、コンパイルできません。

というわけで、JetBrains様がIntelliJ IDEAを対応させるのを待つしかなさそうです。

IntelliJガチ勢の僕としては、googleに一言もの申したい

googleさん、JetBrains様にごめんなさいしてください

つーか、ツールがバージョンアップしてもIDEのバージョンアップが必要ないと言ってた、

あのリリースノート何だったんです?

google 「ツールがバージョンアップしてもIDEのバージョンアップがいらないと言ったな、あれは、嘘だ」

AndroidがJavaだと思っている人のためのGroovy講座 - 2

こんいちわ。みけです。

またも、タイトルが仰々しくてすみません。

本当に大したこと書かないです。

で、相変わらずAndroidまたもや出て来ません。

gradleも出て来ません。

でも、Android Studioの登場や、gradle-android-pluginで

gradleに興味を持たれた方には読んで貰いたいと思います。

以下、本題。

Closure<V>とはなんぞや

Closureについて僕がなんか言うと、

皆が混乱するので(それだけ僕もちゃんと説明できるほど理解していない…orz)

とりあえずWikipediaのClosureでも読んで下さい。

Javaでいえば、Callable<V>みたいなものです。

で、Callable<V>に比べて便利なのが

1
2
3
4
5
6
7
Callable<String> callable = new Callable<String>() {
    @Override
    public String call() {
        // some codes
        return result.toString();
    }
};

みたいに書かなくてよいあたりです。

ちなみにCallable<T>と異なるところもあります。

それは、おいおい、説明します。

そろそろClosure<V>のサンプル見せてくれよ

というわけで、適当にサンプルをみつくろってみました。

単純な値を返すだけのClosure

1
2
3
4
5
def closure = {
    'Hello, Closure!'
}

assert closure() == 'Hello, Closure!'

そうそう、groovyではreturnを省略することができます

その場合、最後に評価された式の値がreturnされます。

上記のClosureではClosureの最終式'Hello, Closure!'が評価され、

その値'Hello, Closure!'が返されます。

引数をとるClosure

1
2
3
4
5
6
def hello = {name ->
    'Hello, ' + name
}

assert 'Hello, mike' == hello('mike')
assert 'Hello, null' == hello()

Closure<V>Callable<V>との決定的な違いが、 (大した違いではないが)

引数を与えることができる点です。

これをCallable<V>でやろうとすると次のようになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class HelloTest {

    public class Hello implements Callable<String> {

        private final String name;

        public Hello(String name) {
            this.name = name;
        }

        @Override
        public String call() throws Exception {
            return "Hello, " + name;
        }
    }

    @Test
    public void testHello () throws Exception {
        assertThat(new Hello("mike").call(), is("Hello, mike"));
        assertThat(new Hello(null).call(), is("Hello, null"));
    }
}

Closureを返すClosure

1
2
3
4
5
6
7
8
9
10
11
def counter = {int offset ->
    return {
        offset++
    }
}

def closure = counter(0)

(0..100).each {
    assert it == closure()
}

初期値の0からカウントするカウンターのようなクロージャーが返ってきます。

型としてはClosure<Closure<Integer>>といったところでしょうか…

で、これをみると一つ気持ち悪いところがありますね。

元の値ってどうなってしまうの?

1
2
3
4
5
6
7
8
9
10
11
12
def closure = {String hello ->
    return {String name ->
        hello += name
    }
}

def original = 'Hello, '
def message = closure(original)

assert message('mike') == 'Hello, mike'
assert message('!') == 'Hello, mike!'
assert original == 'Hello, '

というように、元の値は壊れませんが、

返されるClosureが元々持っていた値は壊れていきます。

ところで、イテレーターとしてClosure<V>を使うときに出てくる

itってなんやねん?

gradleのプラグイン宣言などで、ときどきこんな記述が出てきますね

1
2
3
['java', 'groovy'].each {
    apply plugin : it
}

で、このitですが、コレクションの一つ一つの要素です。

あ、そんなこと言われなくても知ってたって(´・ω・`)

まあ、そんなことは言わずに、すこし試してみましょう。

1
2
3
(1..4).each {
    println "it is ${it}."
}

実行結果はこんな感じです。

1
2
3
4
it is 1.
it is 2.
it is 3.
it is 4.

これって、でもどういうふうに呼ばれているの?

groovyのDefaultGroovyMethodsクラスを介して呼ばれています。

自分でもitでアクセスできるイテレーター作ってみたい

というわけで、Closure<V>を引数にとるメソッドを書いてみました。

itで要素にアクセスできて、ちょっと嬉しいですね。

1
2
3
4
5
6
7
8
9
10
11
Integer.metaClass.define {
    collect = {Closure closure ->
        return new IntRange(0, delegate).collect {
            closure(it)
        }
    }
}

assert 2.collect {
    it * 2
} == [0, 2, 4]

というわけで、itは一つ一つの要素であることが事象として理解していただけたと思います。

で、itっていつ割り当てられるの?ということで、ソースをざっと見ていたんですが、

見つかりませんでした(´・ω・`)

なお調べている過程で僕も初めて知ったのですが、

Closure<V>は抽象クラスで、

コンパイル時に実際のクラスが生成されているようです。

まだまだ僕も勉強が足りませんね…

で、このへんが気になりだすと、もっと気になるのが…

Closureのスコープってどうなのよ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def outside = 'This is outside'
def doSomething = {println 'do something'}
def closure = {
    def inside = 'This is inside'
    doSomething()
    println outside
    println inside
}

closure()

doSomething()
println outside
println inside

これを実効すると次のような結果が得られます。

1
2
3
4
5
6
7
8
9
10
11
12
13
do something
This is outside
This is inside
do something
This is outside
Exception thrown
5 31, 2013 8:25:06 午後 org.codehaus.groovy.runtime.StackTraceUtils sanitize
WARNING: Sanitizing stacktrace:
groovy.lang.MissingPropertyException: No such property: inside for class: ConsoleScript83
  at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:50)
  at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:49)
  at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:231)
  at ConsoleScript83.run(ConsoleScript83:14)

Closure<V>closureの中からは、

フィールドoutsideを参照することや、doSomethingを実行することはできますが、

スクリプト本体からはclosure内部のinsideにアクセスすることはできません。

まあ、大体ご想像されていたとおりだと思います。

実はCallable<V>だったClosure<V>

さて、ここまではCallable<V>との対比でClosure<V>を見て来ましたが、

Closure<V>は実はCallable<V>を実装した抽象クラスです。

まず、Closure<V>のクラスファイルを見てみましょう。

Closure.java
1
2
3
public abstract class Closure<V> extends GroovyObjectSupport implements Cloneable, Runnable, GroovyCallable<V>, Serializable {
    // some codes
}

Closure<V>はインターフェースGroovyCallable<V>を実装しています。

で、このGroovyCallable<V>ってなんやねんというと…

GroovyCallable.java
1
public interface GroovyCallable<V> extends Callable<V> { }

ってことで、Callable<V>を継承しているインターフェースになります。

まとめるとClosure<V>Callable<V>の実装クラスということになります。

では試してみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.concurrent.*

def service = Executors.newFixedThreadPool(3)

def callable = {int sec ->
    return {
        println "${new Date().format('yyyy/MM/dd hh:mm:ss')} : Sleeping ${sec} seconds..."
        Thread.sleep(sec * 1000)
        "${new Date().format('yyyy/MM/dd hh:mm:ss')} : This is ${sec}" as String
    }
}

assert service.invokeAll((10..1).collect {
    callable(it) as Callable<String>
}).each {
    println it.get()
}.size() == 10

この実行結果は次のようになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2013/05/31 02:13:56 : Sleeping 10 seconds...
2013/05/31 02:13:56 : Sleeping 8 seconds...
2013/05/31 02:13:56 : Sleeping 9 seconds...
2013/05/31 02:14:04 : Sleeping 7 seconds...
2013/05/31 02:14:05 : Sleeping 6 seconds...
2013/05/31 02:14:06 : Sleeping 5 seconds...
2013/05/31 02:14:11 : Sleeping 4 seconds...
2013/05/31 02:14:11 : Sleeping 2 seconds...
2013/05/31 02:14:11 : Sleeping 3 seconds...
2013/05/31 02:14:13 : Sleeping 1 seconds...
2013/05/31 02:14:06 : This is 10
2013/05/31 02:14:05 : This is 9
2013/05/31 02:14:04 : This is 8
2013/05/31 02:14:11 : This is 7
2013/05/31 02:14:11 : This is 6
2013/05/31 02:14:11 : This is 5
2013/05/31 02:14:15 : This is 4
2013/05/31 02:14:14 : This is 3
2013/05/31 02:14:13 : This is 2
2013/05/31 02:14:14 : This is 1

全然、問題なくCallable<V>として動いているのが確認できると思います。

で、別にここまではなんだjavaじゃん、というわけですが、

ここからがほぼ本題です。

delegate

delegateがgroovyのClosure<V>の柔軟性をもたらしていることに疑いはありません。

まずは使用例から…

groovy.xml.MarkupBuilder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import groovy.xml.*

def w = new StringWriter()
def doc = new MarkupBuilder(w)

def border = 'border : solid 1px #ccc;'
def background = 'background-color : rgba(239, 239, 255, 0.7);'
def padding = 'padding : 5px;'
def radius = 'border-radius : 5px;'

doc.html(lang : 'ja') {
    head {
        title 'test page'
    }
    body {
        h3 'Hello Groovy!'
        p (style : "${border}${background}${padding}${radius}",
                'This document is made by groovy.')
    }
}

println w.toString()

これを実効すると、次のようなhtmlファイルができます。

1
2
3
4
5
6
7
8
9
<html lang='ja'>
  <head>
    <title>test page</title>
  </head>
  <body>
    <h3>Hello Groovy!</h3>
    <p style='border : solid 1px #ccc;background-color : rgba(239, 239, 255, 0.7);padding : 5px;border-radius : 5px;'>This document is made by groovy.</p>
  </body>
</html>

MarkupBuilderというクラスはgroovy.util.BuilderSupportクラスを継承していて、

存在しないようなメソッドを実行する際に、

1
protected Object doInvokeMethod(String methodName, Object name, Object args)

を介して実行します。

そして、このメソッドが内部でNodeを作っていきます。

そういうわけで、上記のコードの11行目のhtmlメソッドが実行されて、

"html"というノードが作成されるのはわかるかと思います。

問題はhtmlというメソッドが引数として受け入れたClosure<V>

headというメソッドやbodyメソッドを実行しています。

さきほどのClosureのスコープに戻るとこの場合、

headbodyというメソッドはないので、

ここでMissingMethodExceptionが発生しそうですが、

発生しません。

何故でしょうか?

これの謎を解く鍵がdelegateです。

Closure#setDelegateClosure#setResolveStrategy

Closure<V>には変数名やメソッド名をどのオブジェクトから参照するのかを決定することができます。

そのオブジェクトへの参照がdelegateになります。

では、delegateをわざとらしいほど強調したスクリプトを見てみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
setProperty 'x', 1000
setProperty 'y', 500
setProperty 'exec', {println "x : ${x}, y : ${y}"}

def map = [x : 10, y : 5, exec : {println "x = ${x}, y = ${y}"}]
def closure = {
    println "x -> ${x}"
    println "y -> ${y}"
    print 'delegate exec -> '
    exec.call()
    print 'owner exec -> '
    exec()
}
closure.delegate = map
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure()

では、実行してみましょう。

1
2
3
4
x -> 10
y -> 5
delegate exec -> x = 1000, y = 500
owner exec -> x : 1000, y : 500

面白い結果が返ってきました。

closure内部でxyの値はmapから取得されたものになっています。

一方、execに関しては二つの結果が出てきています。

これは、15行目と16行目で行われているclosureのdelegateの設定によって説明出来ます。

closureがフィールド名を解決する仕組みは下記のようになっています。

  • Closure.DELEGATE_FIRSTでdelegateされたオブジェクトから順番に解決していく
  • delegateされたオブジェクトmapから変数を解決する

ここから、7行目、8行目のxymap由来のものであることがわかります。

また10行目と12行目のexecが異なる結果となっているのは、

10行目のexecはフィールド名としてまず解決された後に、

Closure<V>が実行されています。

一方、12行目のexecではメソッドとして(map(クラスはjava.util.Map)にはメソッドexecがない)

名前解決をします。そのためclosureOWNERであるスクリプトの方のexecが参照されます。

また、10行目と12行目のexecが参照するxyが1行目と2行目で設定されているxyなのは

次の理由からです。

  • map.execのdelegateが設定されてない
  • execのdelegateが設定されていない
MarkupBuilder再び

さきほどのMarkupBuilderに戻ります。

MarkupBuilder#doInvokeMethod(String, Object, Object)メソッドでは

どのような処理がされているのかというと…

MarkupBuilder.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// this is partial codes
protected Object doInvokeMethod(String methodName, Object name, Object args) {
    Object node = null;
    Closure closure = null;

    // creating node and attach it to variable node.
    // getting closure from arg and attach it to variable closure.
    if (closure != null) {
        // stack operation
        setClosureDelegate(closure, node);
        closure.call();
        // stack operation
    }
    // node create completion and return it
}
protected void setClosureDelegate(Closure closure, Object node) {
    closure.setDelegate(this);
}

9行目でsetClosureDelegateメソッドを呼び出し、

15行目からのsetClosureDelegateでは、

closureのdelegateにthis(つまりMarkupBuilderのインスタンス)を割り当てています。

その結果10行目のclosure.call()では、

メソッドおよびフィールドの名前解決がMarkupBuilderのインスタンスから行われるます。

その結果、MarkupBuilderの例でheadとかbodyといったメソッドが、

MarkupBuilderのインスタンスに対して呼び出されるということになります。

さて、翻って

gradleのtaskメソッド…

でも、Closure<V>が使われていますね。

build.gradle
1
2
3
4
5
6
7
task myTask {
    description = 'this is myTask'.
    println 'this is not task, but configuration.'
    doLast {
      println 'finished myTask'
    }
}

gradleのandroidサポートが本格的になってからgradleを始めた人の中には

上記のようなgradleのProjectクラスのメソッドtask(Object, Closure)

Closure部分をタスクだと思っている人がかなりの割合でいると思いますが、

このClosure部分はタスクではなく、設定です

こう書いた場合にタスクはgradleコマンドを書いた時に必ず実行されると

言っていると、元からgroovyをやっている人とは話がかみあわなくなるので、

気をつけて下さい。

では、ちょっとソースコードを覗いてみます。

AbstractProject.java
1
2
3
4
// this is partial codes
public Task task(String task, Closure configureClosure) {
    return taskContainer.create(task).configure(configureClosure);
}

AbstractProject#task(String, Closure)では、まずTaskが作成された後に、

Task#configure(Closure)が呼び出されます。

AbstractTask.java
1
2
3
4
// this is partial codes
public Task configure(Closure closure) {
    return ConfigureUtil.configure(closure, this, false);
}

Task#configure(Closure)ではConfigureUtil#configure(Closure, Task, boolean)が呼び出されます。

ConfigureUtil.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// this is partial codes
public static <T> T configure(Closure configureClosure,
        T delegate,
        boolean configureableAware) {
    return configure(configureClosure,
            delegate,
            Closure.DELEGATE_FIRST,
            configureableAware);
}
private static <T> T configure(Closure configureClosure,
        T delegate,
        int resolveStrategy,
        boolean configureableAware) {
    ClosureBackedAction<T> action = new ClosureBackedAction<T>(
            configureClosure,
            resolveStrategy,
            configureableAware);
    action.execute(delegate);
    return delegate;
}

ここから、org.gradle.api.internal.ClosureBackedAction<T>により、

Closureが実行されます。

ClosureBackedAction.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// this is partial codes
public ClosureBackedAction(Closure closure,
        int resolveStrategy,
        boolean configureableAware) {
    this.closure = closure;
    this.configureableAware = configureableAware;
    this.resolveStrategy = resolveStrategy;
}
public void execute(T delegate) {
    // check closure is not null
    // checking cinfgureableAware is false
    Closure copy = (Closure) closure.clone();
    copy.setResolveStrategy(resolveStrategy);
    copy.setDelegate(delegate);
    if (copy.getMaximumNumberOfParameters() == 0) {
        copy.call();
    } else {
        copy.call(delegate);
    }
}

12〜14行目でdelegateの設定、16行目or17行目でClosureの実行がなされていますね。

で、Closureのdelegateにはタスクが設定されています。

したがって、少し前で説明した通り、

Closureの実行において変数名の解決はTaskのインスタンスから順番に解決されていきます。

で、これが最終的にタスクの設定になるわけです。

(結構たらい回しにされていてイライライする方がいらっしゃるかもしれませんが、まあ、テスタビリティを上げるためにこうなっています。)

というわけで、まとめ

Closure<V>の仕組みがわかるとGradle DSLの理解が容易になります。

冒頭でgradleの話しませんって言ったな、あれは、嘘だ。

AndroidがJavaだと思っている人のためのGroovy講座 - 1

タイトルが偉そうなこと書いていますが、

大したことは書きません。

みけです。

Android Studioが出てきてgradleとは何ぞと思っている方がいると思います

gradleはgroovyで記述できるビルドシステムです。

antの自由なところと、mavenの依存性管理を組み合わせたイケてるところが売りです。

さて、この記事では…

gradleのことはほとんど書きません

Android開発者でgradleとは何やねん?って思っている方には

こちらに詳細が書かれていますので、読んでみて下さい。

で、この記事は何を書いているの?というわけで、

簡単なgroovyの文法の説明を書きます

これを覚えると、gradleの記述で少し楽をできるかもしれません。

でも、groovyのことを完璧に説明しているかというと、そうでもないので、

詳しくは次の本を買って読んで下さい。

では、以下、本題

メソッドの引数部分の括弧は省略できる

多分gradleを初めて見た人で何やねんこれと思う記述はこういうやつだと思います。

build.gradle
1
task myTask

このtaskというのは、AbstractProjectという抽象クラスで定義されているpublic Task task(Object task)というメソッドです。このメソッドはタスクをプロジェクトに登録するメソッドです。

したがって、上記の例ではtaskメソッドによってmyTask.toString()で得られる名前(myTask)のタスクが作成されます。

gradleの中で同様な記述がたくさんあると思います。

例えば

build.gradle
1
2
3
4
5
6
repositories {
    // some repository configuration
}
dependencies {
    // some dependency configuration
}

これらrepositoriesや、dependenciesも、実はみんなメソッドです。

裏をとってみる

では、こっから先はちょっと混みいった話

gradleのprojectの元になるクラスはorg.gradle.api.internal.project.AbstractProjectクラスです。

このクラスの中を少し見てみましょう。

AbstractProject.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public abstract class AbstractProject
        extends AbstractPluginAware
        implements ProjectInternal, DynamicObjectAware {
    // some codes

    public void repositories(Closure configureClosure) {
        ConfigureUtil.configure(configureClosure, getRepositories());
    }

    public void dependencies(Closure configureClosure) {
        ConfigureUtil.configure(configureClosure, getDependencies());
    }

    // some codes

    public Task task(String task) {
        return taskContainer.create(task);
    }

    public Task task(Object task) {
        return taskContainer.create(task.toString());
    }

    // some codes
}

はい、dependenciesrepositoriestaskといったキーワードはすべてメソッドですね。

結論:groovyではメソッドの引数部分の括弧を省略できる

…ん、gradleのことほとんど書かないと言ったな、あれは嘘だ。

Blogの更新pingを送れるようにrakeのタスクを書いてみた

こんにちわ、みけです。

お金がないので、ブログを読んでもらって、

google adsenseをみんなに押してもらわないと、

死んでしまいます。

さて、そんなことはどうでもよくて、

bloggerでブログをやっていたときは、

googleさんが勝手に検索エンジンに乗せてくれるので、

検索しやすかったのですが、

githubでブログを書くようになって、

検索で出にくくなっていたので、

更新pingをrakeで送りつけるようにしました。

まあ、「更新ping ruby」でググった結果をrakeのタスクにしただけですが…

で、追加したのがこんな感じのタスクです。

rakefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
require "yaml"
require "xmlrpc/client"

#-- sending ping --#
desc "Sedning ping to Web Search Engines"
task :ping do
  site_config = YAML.load(IO.read('_config.yml'))
  blog_title = site_config['title']
  blog_url = site_config['url']
  ping_url = YAML.load(IO.read('ping.yml'))
  ping_url.each do |url|
    ping = XMLRPC::Client.new2(url)
    begin
      result = ping.call('weblogUpdates.ping', blog_title, blog_url)
      puts "#{url} : #{result}"
    rescue => e
      puts "#{url} : #{e}"
    end
  end
end

この新たに追加したタスクを後は、gen_deployタスクに追加します。

rakefile
1
2
3
desc "Generate website and deploy"
task :gen_deploy => [:integrate, :generate, :deploy, :ping] do
end

あと、適当にping.ymlに更新pingを送りつけるサイトを記述すればおkです。

ping.yml
1
2
3
4
5
6
7
8
9
10
- http://blogsearch.google.com/ping/RPC2
- http://api.my.yahoo.co.jp/RPC2
- http://blog.goo.ne.jp/XMLRPC
- http://ping.bloggers.jp/rpc/
- http://ping.rss.drecom.jp/
- http://ping.fc2.com/
- http://rpc.weblogs.com/RPC2
- http://rpc.reader.livedoor.com/ping
- http://ping.blogranking.net/
- http://www.blogpeople.net/ping/

gradleとJUnitのEnclosedの話

みけです。

先ほど書いたエントリーでテストをgradleで走らせたのですが、

という状態が発生してました。

具体的には、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package org.mikeneck.multithreads;

import org.junit.*;
import org.junit.experimental.runners.Enclosed;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

/**
 * @author mike
 */
@RunWith(Enclosed.class)
public class SimpleSocketTest {

    public static class SingleClient {

        private static final ExecutorService SERVICE = Executors.newFixedThreadPool(1);

        private static final int PORT = 12521;

        private static final String LOCALHOST = "localhost";

        private SimpleClient client;

        @Rule
        public TestName testName = new TestName();

        @BeforeClass
        public static void start () throws IOException {
            SERVICE.execute(new SimpleServer(PORT));
        }

        @Before
        public void setup () throws IOException {
            client = new SimpleClient(LOCALHOST, PORT);
        }

        @After
        public void tearDown () throws Exception {
            System.out.println(testName.getMethodName() + " is closing");
            client.close();
        }

        @AfterClass
        public static void end () throws IOException {
            new SimpleClient(LOCALHOST, PORT).open().bye();
        }

        @Test
        public void socketProcessing () throws IOException {
            client.open();
            String message = client.sendMessage("Hello");
            System.out.println("Message from Server [" + message + "]");
            assertThat(message, is("Hello"));
            System.out.println("Assertion ends.");
        }
    }
}

というテストに対して、

という感じで、謎のclassMethodというテストが追加されていて、

実行されてテストが落ちてしまうようです。

先駆者はいた

とりあえず、gradleEnclosedJunitでググっていたところ、

次の二つのエントリーを発見しました。

というわけで、gradleEnclosedの相性がわるいっぽい…

gradleでEnclosedなテストをする時の対策

というわけで、@shuji_w6eさんのページによると

テストの実行時に除外クラスを指定すること。

だそうです。

build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apply plugin : 'groovy'
apply plugin : 'idea'

group = 'org.mikeneck.multithreads'
version = '1.0'

def compatibility = 1.7

sourceCompatibility = compatibility
targetCompatibility = compatibility

repositories {
  mavenCentral ()
}

dependencies {
  compile 'org.codehaus.groovy:groovy-all:2.1.3'
  testCompile ('junit:junit:4.11') {
      exclude module : 'hamcrest-core'
      exclude module : 'hamcrest'
  }
  testCompile 'org.hamcrest:hamcrest-library:1.+'
}

test {
    exclude '**/*$*'
}

とりあえず、テストクラスを除外してみました。

結果

という感じで、テストが通りました。

JavaではじめてSocketプログラムを書いてみた

みけです。

jjugのメーリングリストにconcurrentに関する質問が着ていたので、

答えようと思ってみたんですが、

Socketを使ったプログラムで、

そういえば僕、Socketを使ったプログラム書いたこと無いな

と思い至り、書いてみることにしました。

SocketServerのほう

java.net.ServerSocketを使って書くようです。

SimpleServer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package org.mikeneck.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author mike_neck
 */
public class SimpleServer implements Runnable {

    private final ServerSocket serverSocket;

    public SimpleServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    @Override
    public void run() {
        while(true) {
            try (Socket socket = serverSocket.accept();
                 BufferedReader reader = new BufferedReader(
                     new InputStreamReader(socket.getInputStream()));
                 PrintStream writer = new PrintStream(socket.getOutputStream())
            ) {
                String line = reader.readLine();
                writer.println(line);
                if (line.equals("BYE")) {
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

SocketClientのほう

クライアントアプリケーションの方はjava.net.Socketを用いるようです。

SimpleClient.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package org.mikeneck.multithreads;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * @author mike_neck
 */
public class SimpleClient implements AutoCloseable {

    private final Socket socket;

    private DataOutputStream output;

    private BufferedReader input;

    public SimpleClient(String hostname, int port) throws IOException {
        socket = new Socket(hostname, port);
    }

    public SimpleClient open () throws IOException {
        output = new DataOutputStream(socket.getOutputStream());
        input = new BufferedReader(
            new InputStreamReader(socket.getInputStream()));
        return this;
    }

    public String sendMessage(String message) throws IOException {
        output.writeBytes(message + '\n');
        String line;
        if ((line = input.readLine()) != null) {
            return line;
        } else {
            return "";
        }
    }

    public void bye () throws IOException {
        output.writeBytes("BYE\n");
    }

    @Override
    public void close() throws Exception {
        if (!socket.isClosed()) {
            socket.close();
        }
    }
}

SocketServerでの疑問

と、まあ簡単なプログラムを書いたのですが、

二回メッセージを送る場合はどうなるのかとか、疑問が残りますね。

で、テストを書くわけですが…

案の定落ちました。

SendManyMessages.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// import とか packageとか省略
public static class SendManyMessages {

    private static final ExecutorService SERVICE = Executors.newFixedThreadPool(1);

    private static final int PORT = 14541;

    private static final String LOCALHOST = "localhost";

    @Rule
    public TestName testName = new TestName();

    @BeforeClass
    public static void start () throws IOException {
        SERVICE.execute(new SimpleServer(PORT));
    }

    @AfterClass
    public static void end () throws IOException {
        new SimpleClient(LOCALHOST, PORT).open().bye();
    }

    @ThisTestWillFail
    @Test
    public void send2times () throws IOException {
        SimpleClient client = new SimpleClient(LOCALHOST, PORT).open();

        assertThat(client.sendMessage("hello"), is("hello"));
        assertThat(client.sendMessage("good-bye"), is("good-bye"));
    }
}
SendManyMessages.java
1
2
3
4
5
6
7
java.net.SocketException: Broken pipe
  at java.net.SocketOutputStream.socketWrite0(Native Method)
  at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109)
  at java.net.SocketOutputStream.write(SocketOutputStream.java:132)
  at java.io.DataOutputStream.writeBytes(DataOutputStream.java:276)
  at org.mikeneck.multithreads.SimpleClient.sendMessage(SimpleClient.java:31)
  at org.mikeneck.multithreads.SendManyMessages.send2times(SendManyMessages.java:37)

うん、まあ、今回は別になんか状態を持つようなサーバーを作りたいわけではないし、

そういうサーバーを作りたい場合は、まあ、そういう工夫をすればいいということだけ覚えておこう。

なお、他に書いたテストもありますが、

面倒なので、gistにあげておきました。

こちらをどうぞ

Erlangのlistsモジュールを試してみる - 第3回

Erlangのlistsモジュールを試してみるの第三回は次のコマンドの結果について試していきます。

1
2
3
4
5
6
7
8
9
10
11
lists:sublist(
    lists:sort(
        lists:map(
            fun({F, A}) ->
                atom_to_list(F) ++
                "/" ++
                integer_to_list(A)
            end,
            lists:module_info(exports))),
    33,
    16).

結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[
  "merge/1",
  "merge/2",
  "merge/3",
  "merge3/3",
  "min/1",
  "module_info/0",
  "module_info/1",
  "nth/2",
  "nthtail/2",
  "partition/2",
  "prefix/2",
  "reverse/1",
  "reverse/2",
  "rkeymerge/3",
  "rmerge/2",
  "rmerge/3"
]

merge/1

Erlang Document

merge(ListOfLists) –> List1

Types

  • ListOfLists = [List]
  • List = List1 = [T]
  • T = term()

Returns the sorted list formed by merging all the sub-lists of ListOfLists. All sub-lists must be sorted prior to evaluating this function. When two elements compare equal, the element from the sub-list with the lowest position in ListOfLists is picked before the other element.

参照先

Explain

リスト内のリストをソートしてマージしたリストを返します。なお、リスト内のリストは事前にソートされている必要があります。

Example

1
2
3
4
5
1> lists:merge([
1> [0,2,4],
1> [3,6,9],
1> [1,5,7]]).
[0,1,2,3,4,5,6,7,9]

複数のリストがマージされて新しいリストが返されます。

merge/2

Erlang Document

merge(List1, List2) –> List3

Types

  • List1 = [X]
  • List2 = [Y]
  • List3 = [(X | Y)]
  • X = Y = term()

Returns the sorted list formed by merging List1 and List2. Both List1 and List2 must be sorted prior to evaluating this function. When two elements compare equal, the element from List1 is picked before the element from List2.

参照先

Explain

二つのリストをソートしてマージした新しいリストを返します。二つのリストは事前にソートされている必要があります。

Example

1
2
1> lists:merge([1,3,4,6],[0,2,5,7]).
[0,1,2,3,4,5,6,7]

二つのリストがマージされた新しいリストが返されます。

merge/3

Erlang Document

merge(Fun, List1, List2) –> List3

Types

  • Fun = fun((A, B) –> boolean())
  • List1 = [A]
  • List2 = [B]
  • List3 = [(A | B)]
  • A = B = term()

Returns the sorted list formed by merging List1 and List2. Both List1 and List2 must be sorted according to the ordering function Fun prior to evaluating this function. Fun(A, B) should return true if A compares less than or equal to B in the ordering, false otherwise. When two elements compare equal, the element from List1 is picked before the element from List2.

参照先

Explain

List1List2をソートしてマージした新しいリストを返します。引数のリストは事前にソートされている必要があります。Fun(A, B)はソートオーダーに関してAが先に来るべきである場合はtrueを、そうでない場合はfalseを返します。同じ値の要素がある場合は、List1のものがList2のものに優先されます。

Example

1
2
3
4
5
6
1> lists:merge(fun(A, B) -> A > B end,[6,4,3,1],[7,5,2,0]).
[7,6,5,4,3,2,1,0]
2> lists:merge(fun({A,_}, {B,_}) -> A =< B end,
2> [{1,a},{3,a},{4,a},{5,a}],
2> [{0,b},{2,b},{4,b},{6,b}]).
[{0,b},{1,a},{2,b},{3,a},{4,a},{4,b},{5,a},{6,b}]

最初の例では、降順にソートする関数で評価してリストをマージして新しいリストを返します。

後者の例では、昇順ソートです。同一の値の場合にList1から要素が取得されています。

merge3/3

Erlang Document

merge3(List1, List2, List3) –> List4

Types

  • List1 = [X]
  • List2 = [Y]
  • List3 = [Z]
  • List4 = [(X | Y | Z)]
  • X = Y = Z = term()

Returns the sorted list formed by merging List1, List2 and List3. All of List1, List2 and List3 must be sorted prior to evaluating this function. When two elements compare equal, the element from List1, if there is such an element, is picked before the other element, otherwise the element from List2 is picked before the element from List3.

参照先

Explain

三つのリストをソートしてマージした新しいリストを返します。引数のリストは事前にソートされている必要があります。もし同じ値の要素があった場合はList1List2List3の順番で優先されます。

Example

1
2
3
4
5
50> lists:merge3(
50>   [2, 5, 8],
50>   [1, 4, 9],
50>   [3, 6, 7]).
[1,2,3,4,5,6,7,8,9]

min/1

Erlang Document

min(List) –> Min

Types

  • List = [T, …]
  • Min = T
  • T = term()

Returns the first element of List that compares less than or equal to all other elements of List.

参照先

Explain

リスト中、最初に現れた最も小さい要素を返します。

Example

1
2
3
4
5
6
7
8
9
10
1> lists:min([5, 3, 1, 4, 5]).
1
2> lists:min([h, b, n, t, l]).
b
3> lists:min([0,0.0]).
0
4> lists:min([0.0,0]).
0.0
5> lists:min(['A', 123, a, 123.0]).
123

最初の例では引数のリストの最小整数1が返されます。

次の例では引数のリストの最小のatombが返されます。

三番目と四番目の例では整数と浮動小数点数の0を比較して最初に現れた要素が返されます。

最後の例では整数と浮動小数点数とatomを比較しています。最初に現れた最小の要素(整数)が返されます。

module_info/0

Erlang Document

module_info

The module_info/0 function in each module returns a list of {Key,Value} tuples with information about the module. Currently, the list contain tuples with the following Keys: attributes, compile, exports, and imports. The order and number of tuples may change without prior notice.

warning

The {imports,Value} tuple may be removed in a future release because Value is always an empty list. Do not write code that depends on it being present.

参照先

Explain

これはlistsモジュールでなく、すべてのモジュールに共通する関数ですな。 モジュールに関する情報を{Key, Value}のタプルリストで返します。現在のところ、

  • attributes
  • compile
  • exports
  • imports

といったキーです。なお、{imports, Value}タプルは常に空のリストしか返さないため、今後なくなる予定です。これに基づいたコードを記述しないで下さい。

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1> lists:module_info().
[{exports,[{append,2},
           {append,1},
           {subtract,2},
           {nth,2},
           {nthtail,2},
           {prefix,2},
           {suffix,2},
           {last,1},
           {seq,2},
           {seq,3},
           {sum,1},
           {duplicate,2},
           {min,1},
           {max,1},
           {sublist,3},
           {sublist,2},
           {zip,2},
           {unzip,1},
           {zip3,3},
           {unzip3,1},
           {zipwith,3},
           {zipwith3,4},
           {merge,1},
           {merge3,3},
           {rmerge3,...},
           {...}|...]},
 {imports,[]},
 {attributes,[{vsn,[257948301539042745638557295194154171573]}]},
 {compile,[{options,[{outdir,"/net/isildur/ldisk/daily_build/r16b_prebuild_master-opu_o.2013-02-25_20/otp_src_R16B/lib/stdlib/src/../ebin"},
                     {i,"/net/isildur/ldisk/daily_build/r16b_prebuild_master-opu_o.2013-02-25_20/otp_src_R16B/lib/stdlib/src/../include"},
                     {i,"/net/isildur/ldisk/daily_build/r16b_prebuild_master-opu_o.2013-02-25_20/otp_src_R16B/lib/stdlib/src/../../kernel/include"},
                     warnings_as_errors,debug_info]},
           {version,"4.9"},
           {time,{2013,2,25,19,27,47}},
           {source,"/net/isildur/ldisk/daily_build/r16b_prebuild_master-opu_o.2013-02-25_20/otp_src_R16B/lib/stdlib/src/lists.erl"}]}]

たくさん出てきた…

module_info/1

Erlang Document

module_info

The call module_info(Key), where key is an atom, returns a single piece of information about the module.

参照先

Explain

module_info関数からKeyを指定して取り出します。

Example

1
2
1> lists:module_info(attributes).
[{vsn,[257948301539042745638557295194154171573]}]

lists:module_info/0の結果の一部が返ってきます。

nth/2

Erlang Document

nth(N, List) –> Elem

Types

  • N = integer() >= 1
  • List = [T, …]
  • Elem = T
  • T = term()

Returns the Nth element of List.

参照先

Explain

N番目の要素を返します。

Example

1
2
1> lists:nth(3,[a,2,c,4,e,5]).
c

3番目の要素cが返されます。

nthtail/2

Erlang Document

nthtail(N, List) –> Tail

Types

  • N = integer() >= 0
  • List = [T, …]
  • Elem = T
  • T = term()

Returns the Nth tail of List, that is, the sublist of List starting at N+1 and continuing up to the end of the list.

参照先

Explain

N+1番から後ろの要素で作られるサブリストを返します。

Example

1
2
1> lists:nthtail(3,[a,2,c,4,e,5]).
[4,e,5]

4番目から後ろの要素で構成されるリストが返されます。

partition/2

Erlang Document

partition(Pred, List) –> {Satisfying, NotSatisfying}

Types

  • Pred = fun((Elem :: T) –> boolean())
  • List = Satisfying = NotSatisfying = [T]
  • T = term()

Partitions List into two lists, where the first list contains all elements for which Pred(Elem) returns true, and the second list contains all elements for which Pred(Elem) returns false.

参照先

Explain

条件と分割対象リストを引数に取り、二つの新しいリストに分割します。最初のリストには条件を満たすものを、次のリストには条件を満たさないものを返します。

Example

1
2
1> lists:partition(fun(X) -> X rem 2 =:= 0 end, [0,1,2,3,4,5,6,7]).
{[0,2,4,6],[1,3,5,7]}

偶数かどうか判定する条件とリストを与えています。 先頭のリストには条件を満たすもの(偶数)のリスト、後ろ側のリストには条件を満たさないもの(奇数)のリストが返されます。

prefix/2

Erlang Document

prefix(List1, List2) –> boolean()

Types

  • List1 = List2 = [T]
  • T = term()

Returns true if List1 is a prefix of List2, otherwise false.

参照先

Explain

List1List2の先頭部分である場合にtrueを、異なる場合にはfalseを返します。

Example

1
2
3
4
5
6
1> lists:prefix([1,2,3,4], [1,2,3,4,5,6,7]).
true
2> lists:prefix([a,b,c,d], [a,z,b,y,c,x]).
false
3> lists:prefix("co", "coexistence").
true

最初の例では、第一引数が第二引数の先頭部分と一致するのでtrueが返されます。

次の例では、第一引数が第二引数の先頭部分と異なるのでfalseが返されます。

最後の例では"co""coexistence"の最初の文字列部分と同じなので、trueが返されます。

reverse/1

Erlang Document

reverse(List1) –> List2

Types

  • List1 = List2 = [T]
  • T = term()

Returns a list with the elements in List1 in reverse order.

参照先

Explain

リストを逆順にして返します。

Example

1
2
1> lists:reverse([9,8,7,6,5]).
[5,6,7,8,9]

リストの順番が逆になって返されています。

reverse/2

Erlang Document

reverse(List1, Tail) –> List2

Types

  • List1 = [T]
  • Tail = term()
  • List2 = [T]
  • T = term()

Returns a list with the elements in List1 in reverse order, with the tail Tail appended.

参照先

Explain

リストを逆順にした上で、第二引数を追加した新しいリストを返します。

Example

1
2
3
4
5
6
1> lists:reverse([5,4,3,2,1],[0]).
[1,2,3,4,5,0]
2> lists:reverse([5,4,3,2,1],[a,b,c]).
[1,2,3,4,5,a,b,c]
3> lists:reverse([5,4,3,2,1],0).
[1,2,3,4,5|0]

最初の例、および二番目の例では最初のリストが逆順になされた上で、次のリストが連結された新しいリストが返ってきます。

最後の例では、結果がリストでなくなります。

公式ドキュメントが間違っているっぽいですね…

rkeymerge/3

Erlang Document

なかった\(^o^)/

Example

1
2
3
4
5
6
7
8
9
10
11
12
1> lists:rkeymerge(2,
1>   [{a, 4}, {a, 3}, {a, 1}],
1>   [{b, 5}, {b, 2}, {b, 0}]).
[{b,5},{a,4},{a,3},{b,2},{a,1},{b,0}]
2> lists:rkeymerge(2,
2>   [{a, 1}, {a, 3}, {a, 4}],
2>   [{b, 0}, {b, 2}, {b, 5}]).
[{a,1},{a,3},{a,4},{b,0},{b,2},{b,5}]
3> lists:rkeymerge(2,
3>   [{a, 4}, {a, 3}, {a, 2}, {a, 1}],
3>   [{b, 5}, {b, 2}, {b, 0}]).
[{b,5},{a,4},{a,3},{b,2},{a,2},{a,1},{b,0}]

lists:keymerge/3のreverse版のようです。

  • 引数のリストは逆順にソートされている必要があります。
  • 同一のキーを持つ場合、第二引数のリストの要素が優先されます。

最初の例では、タプルの2番目の値をキーに逆順ソートされたリストが返されます。

次の例では、逆順にソートされていないため、マージがうまくなされていません。

最後の例では、同じキーがある場合の挙動を確認しています。

同じキー値をもつ要素(ここでは、{a,2}{b,2})がありますが、

{b, 2}の方が優先されているのがわかります。

rmerge/2

Erlang Document

なかった\(^o^)/

Example

1
2
3
4
1> lists:rmerge([4,2,0],[5,3,1]).
[5,4,3,2,1,0]
2> lists:rmerge([0,2,4],[1,3,5]).
[1,3,5,0,2,4]

merge/2のリバースバージョン。

最初の例では逆順ソートされたリストを引数として渡して、

逆順でマージされた新しいリストが返されます。

次の例では事前に逆順マージされていないため、

求める結果(逆順にマージされたリスト)が返ってきません。

rmerge/3

Erlang Document

なかった\(^o^)/

Example

1
2
3
4
5
6
7
8
1> lists:merge(fun({_, A}, {_, B}) -> A >= B end,
1>     [{a,9},{a,5},{a,1}],
1>     [{b,7},{b,5},{b,3}]).
[{a,9},{b,7},{a,5},{b,5},{b,3},{a,1}]
2> lists:merge(fun({_, A}, {_, B}) -> A > B end,
2>     [{a,9},{a,5},{a,1}],
2>     [{b,7},{b,5},{b,3}]).
[{a,9},{b,7},{b,5},{a,5},{b,3},{a,1}]

merge/3関数のリバースバージョン。

引数は先頭から

  • Fun = fun((A, B) –> boolean())
  • List1 = [T]
  • List2 = [T]

です。

なお、

  • Funには結果として先にくるものに対してtrueが返すような関数を渡します。
  • List1、List2ともにFunにて事前にソートされている必要があります。
  • T = term()

となっています。

また、同値なものがあった場合は、Funの実装に従います。

Atlassianユーザーグループ #augjに行ってきた

みけです。

Atlassianユーザーグループ@株式会社ロフトワーク(渋谷) #augjに参加してきました。

今回はHipChat特集です。

HipChatとは

Atlassianが提供するグループチャットサービスです。

ユーザー数が5名までは無料で使うことができます。

主に次のような機能があります。

  • 参加人数無制限のグループチャット
  • セキュリティ面が考慮されている
  • emoticonsと呼ばれる顔文字機能が豊富。かつ自分で追加できる。
  • ファイル共有機能(ファイル数無制限、ファイルサイズは50MBまで、画像ファイルの場合すぐに表示される)によって、迅速なコミュニケーションが可能
  • メールや各種携帯(iPhone/Android)、SMSでnotificationを送ることができる
  • Atlassianによって精力的に開発・運用されているので安心して利用できます

セッション内容まとめ

HipChat以前以後

  • メールだとスパムメールとかいろんなプロジェクトのメールが来たりして、まずフィルタリングしないといけないのが面倒い
  • HipChatにGitHubの情報やJenkinsの情報を連携させることによって、HipChatだけ見ればよいようになった

他のチャットツールとの比較

  • 若者の深刻なIRC離れ(サーバ立てるの面倒い)
  • Chatter(by Salesforce)はクソ思い、Apexとか何それ状態でbotを作りづらい、クライアントがAIRで面倒い
  • Campfireは日本語検索がかなり辛い

そして突然のCrucible

  • Crucible単体での運用例
  • Crucibleを使う前までは会議等で実施(つまり面倒い、時間がかかる)
  • レビュー未実施・実施中・実施済みがファイル単位に管理できて、かなり便利

セッション内容などはConfluenceでみられます。

こちらをどうぞ

HipChatいいですね

え、Lingrでも無料で同じ事できるって?

そうですね。すみません。

そういう方はLingr使えばいいと思います。

ノベルティグッズ

ブログを書くと宣言しておけばTシャツがもらえるということで

ちなみに、TシャツはM/S/Lの順番で希望です。

で、なんか開催前にイケメンの人からもらいました。