Lispのカッコは怖くないよ

最近Lispの連れション仲間を増やしたいので、いろんな初見の人に「Lispって知ってる?」と質問して回っています。

そこそこアンテナのある技術者ならLispというのがプログラミング言語の一派を意味しており、それが主に大量のカッコで構成されていることは知っているようなのですが、なんか拒否反応が多いんですよね。

拒否反応というのが、まあ、だいたい

  • 「カッコが多すぎて気がおかしくなる」
  • 「私の人生は大量のカッコに対応するには短すぎる」
  • 「大学でやったけどカッコ死ね。」

みたいなHTML初心者がタグのネストに敗北したみたいな感想ですね…。

まあ、パッと見てそういいたくなる気持ちは分かるんですが、それ自体がよくあるLispに対する誤解と言わざるを得ないです。

事実、Lispプログラミングは大量のカッコを相手にするのですが、誰もカッコの個数なんて見ていません。

Lispのプログラムの構造を読むときは、インデントしか見てません。

カッコを読む側の話

Lispプログラムの例として、下記のregexp-opt-depthという関数(Emacsregexp-opt.el)を見てください。

(defun regexp-opt-depth (regexp)
  (save-match-data
    (string-match regexp "")
    (let ((count 0) start last)
      (while (string-match "\\\\(\\(\\?[0-9]*:\\)?" regexp start)
        (setq start (match-end 0))
        (when (and (not (match-beginning 1))
                   (subregexp-context-p regexp (match-beginning 0) last))
          (setq last start)
          (setq count (1+ count))))
      count)))

Javaなどであれば { やら } などなどもうちょっと可愛げのある記号が出てくるのに、特徴も糞もない大量のカッコとアルファベット+記号の羅列でめまいがしてきますね。

海千山千と言われるLisperはこのカッコの対応を一瞬で読み解ける超人ばかりなのでしょうか?

もちろんNoです。

上記のようなLispプログラムを読む場合は、普通は下記のように思考しながら読んでいきます。(各行の;; 以降が思考内容です。)

;; defunがあるから、関数定義だなあ。
;; その次には関数名がくるはず。regexp-opt-depthという関数の定義をするんだなあ。
;; regexpという引数一つをとるんだなあ。
(defun regexp-opt-depth (regexp)

  ;; ネストされたところからカッコが始まっている。
  ;; 関数の処理の本体がここから始まるんだなあ。
  ;; save-match-dataを使っているんだな。
  (save-match-data

    ;; さらにネストされた位置にstring-matchの呼び出しがあるなあ。
    (string-match regexp "")

    ;; string-matchが終わったらletを呼び出すんだなあ。
    ;; letは変数の定義だからcountが0、startとlastは定義だけしてるんだなあ。
    (let ((count 0) start last)

      ;; 変数の定義(let)の中にwhileの呼び出しがあるなあ。whileはループ文だなあ
      ;; (string-match… 部分がループ条件か。
      (while (string-match "\\\\(\\(\\?[0-9]*:\\)?" regexp start)

        ;; こっからwhileのループ処理本体だなあ。
        ;; setqは変数代入だなあ。start変数の書き換えかあ。
        (setq start (match-end 0))

        ;; ループ処理の最後にwhenで条件を満たした場合のみの処理を書いているなあ。
        ;; インデント的に(and (not …) (subregexp-context-p ...))が条件か。
        (when (and (not (match-beginning 1))
                           (subregexp-context-p regexp (match-beginning 0) last))

          ;; 条件満たしたらsetqを2つやるんだなあ。
          (setq last start)
          (setq count (1+ count))))

      ;; インデントから見て、letの直下の処理で、最後にcountを返却しているんだなあ。
      count)))

上記の読み方のどこにも遠く離れたカッコの個数や対応を数える兆候が見られないことに注目してください。

カッコの対応を知るのにカッコの数ではなく、(が出現するインデント位置しか見ていません。

(局所的に近場のカッコの1つ2つは数えています。ただそれぐらいは通常の人間ができる芸当の範疇だと思います。)

このようにLispプログラムはインデントでしか読まないのが当たり前なので、逆にLispプログラムを書く場合はインデントで構造が読めるように配置しなければ相当読みづらいといえるでしょう。(普通はエディタが勝手にインデントとってくれます)

開きカッコのインデント位置は気にしますが、閉じカッコといえば、上記のインデントを守っていればただの開きカッコのつじつま合わせにしか過ぎないので、一箇所に集められるだけ集められているのがわかるでしょうか? count)))) などとなっている部分がそうですね。

このようにLispプログラムの構造は、構文上の絶対の規制はないものの、インデントで視覚的に示します。考え方はPythonHaskellCoffeeScriptと同じですね。JavaやCでもまともなプログラムなら構文に沿ってインデントをつけるはずです。

カッコを作る側の話

で、インデントで構造を示すのはいいんですが、インデントなぞただの努力目標、本質的にプログラムの構造はカッコの対応で示されます。(意地悪でカッコに反するインデントをすることも可能です。)

海千山千のLISPerはこれら大量のカッコをいちいちバランスとれるように開きの数と閉じの数を数えながら、Lispプログラムを編集しているのかというと、もちろんNoです。エディタの機能でどうにかします。

普通はpareditというカッコのバランスとったままカッコ単位の各種編集ができる便利なEmacsマクロを使います。これによりほとんどカッコの対応を崩さずに編集可能になります。逆に言うとこれがないとLispなんてやってられません。

Lispではすべてがカッコなので、pareditで好きな単位の式を綺麗に抜き取ることも可能です。

カッコ単位の編集に特化している感じですね。

カッコになっていれば、全部同じ方法で編集できるので、Lisperにとってすべてがカッコの世界はとてもハッピーかもしれません。

まとめ

カッコは怖くないよ!!

2014-08-05 追記

すごいブックマークついてる!!

Lispのカッコよりみなさんの忌憚なきコメントのほうがLisp初心者の僕にとっては怖いですね。

nazoking ((はてなブログで((((gyazo)に置かれた(gifアニメ)のURL)をツイッターでシェア)したツイート)を貼り付け)していて、結果gifアニメが動いていないという悲劇

すんません次からはちゃんとやります

Lispの魅力伝えられてないよ

まず、カッコごときでLispのことを食わず嫌いにならないで欲しいと思いました。

マクロとかは後でいいかと。できればハッカーと画家でも読んでもらえれば。

countの後ろのかっこの数

これは普通にカッコの釣り合いをとってくれるエディタで書いていけば、開き-閉じかっこの数なんかはずっと調整が取れている状態になり、気にしなくてもちゃんとなるようになってます。

Pythonとかでいいじゃん

今回書いたのは単にLispのカッコを恐れる必要がない理由だけですので、PythonLispと同じ土俵に立っているわけではありません。

マクロ機能はもちろん、各Lisp系言語ごとの目玉機能も違いがありますからね。

Lispの利点は他の言語で構文木取り扱う場合にASTパーサ持ち出したり、プリプロセッサだの普段使わない道具を持ってこざるをえなかったりしなきゃいけないといったことがないことです。以前のC++のように>>がテンプレート構文としてダメだどうかとかもありません。これはひたすらカッコしかないお陰です。

ひたすらカッコしかないお陰で、カッコ編集機能だけでいろんなLisp系言語(Common LispSchemeClojure、EmacsLisp)をカバーできたりします。これは他の言語には真似できないところでもあるし、永遠にカッコから脱却できない理由でもあるんじゃないでしょうか。

まあ、本質的には木構造的な何かが表せられればいいので、別にカッコじゃなくてもいいと思います。(編集は面倒そうだが)

emacs

今回はEmacsのpareditを紹介しましたが、vimや他IDEでもたぶんまともに編集できる環境はあるはずです。

LOL(let over lambda - http://letoverlambda.com/ )の人はvimで編集しているらしいです。

Clojure

にゃんぱすー!

tanimina: cf. 「S式の読みやすさ」http://blog.practical-scheme.net/shiro/20120823a-s-expression

このエントリの意味ないやん…

Shiroさんのサイトには全ての答えがありますね…

読み方がわかった、怖くなくなった、なるほど など

あなたたちを待っていました!

ほかみなさん

コメントありがとうございました。

ちなみに、私はもっぱら何か考える時はCoffeeScriptで考えます。

ま、普通に 1+2 とか i++ とか書きたいし、関数は (x) -> (y) -> x + y とかすっきり書けるし、分配もあるし、リスト内包記法みたいなのもあるし?

しかしCoffeeScriptでインデントがとち狂い始めた時、Lispのほうが私の木構造への意図がコードに残り続るし、カッコの編集楽でいいなあと思ったりします。

っていうかいろんな言語がわざわざASTとか作んないでさあ、S式に一度なってくれればいいんだよねー。

転職しました

(※古巣への不満とか、そういうのは無しです)

f:id:knjname:20140729003759p:plain

転職しました。

みなさま、その節は大変お世話になりました。

これからも変わらずプログラマとして、皆様に笑顔になっていたけるよう精進してまいります。

(一部の方へ)飲み会など良い機会ありましたら、またご一緒させてください。

FindBugs 3.0.0 (Java8対応) リリースされてた

http://findbugs.sourceforge.net/

FindBugs 3.0.0が昨日リリースされていたようです!(Eclipseプラグインもリリースされています)

Java8対応になりました!

はっきりいって https://mailman.cs.umd.edu/pipermail/findbugs-discuss/2014-May/003995.html このMLのスレッド読んだ時はもう年内は絶望的だと思っていましたが、よかったー。

これまでJava7ターゲットでクラスファイルを出そうがJava8でFindBugsを動かす限り落ちていたので、とても助かります。

で、さっそく私も使い始めましたが、ちゃんと動いているようです。(注意される警告がやや増えた気がする)

多くのJava8移行者にとって、EclipseFindBugsCheckstyleがJava8未対応というのがかなりの痛手でしたが、先月末からEclipse Lunaがリリースされ、そしてFindBugsがリリースされ、状況がかなり改善されてきてますね。

あとはCheckstyleを待つのみです。

(しかし、本当に人の善意に頼ってOSSは成り立っているものだと感じました。)

追記:CheckstyleのJava8対応について

https://github.com/checkstyle/checkstyle/issues/10

sabaka commented on 14 May Hi! It is my GSOC issue. It has to be done till 18th of August, but I'm going to finish in July.

Also, after discuss with team, we decide, that implementation of ignoring option for Java 8 features is painful, and may break grammar. Unfortunately community has to wait for full implementation of Java 8 features.

プロジェクトメンバの立ち位置がよくわかんないですが、これを見る限りでは、とりあえず対応ではなく、ちゃんとしたJava8対応を8月半ばまでに投入予定みたいですね。

Dockerを使い始めて半年弱

Dockerを使い始めて半年弱ぐらい経ったような気がする。

Dockerを知ったきっかけは、はてブTwitterのTLに流れていたのを見たのがきっかけだと思う。

(みんな新しい技術どうやって知ってるの?と思う人はそういうところをこまめに見ればよい。気に入ったブログはFeedlyに登録すればいい。)

仕事では開発サーバにDocker on Ubuntu on KVM on Ubuntu on BM(Bare Metal)な状態で、JenkinsやRedmineなどの開発用サービスコンテナとして使ってる。

(Dockerを使ってJenkinsでビルドとか面白いことは残念ながらやってません。:-P )

Docker meetup Tokyo #3でもあったんだけれども、開発用サーバに使う人は1マシン複数コンテナが普通のようだ。

逆に不確定要素を排除したいプロダクションだと1マシン1コンテナだったりもするらしい。

私が、なぜ開発用サーバにDockerを使っているかといえば、私が以前立てていた開発用サーバは、私が便利とあらば、生産性をあげるために、ガンガン新しいサービスやアプリを入れてしまっていた。結果、そいつらの構成が1つのOS上でぐちゃぐちゃになってしまい、何がどう絡んでいるのか把握しづらいというプチ問題に発展した。

だから、とりあえず各開発用サービスがコンテナとして明確に分離されれば、少しはこのカオス状態がまともになると思い、Dockerを使った。Dockerfileがクソみたいになっても、Dockerが1.0に達していなくて不安な夜を過ごしても、このメリットを何度も自分に言い聞かせた。

やはり良い垣根というのはソフトウェアにおいては大事である。

また、一つの開発用サービスを用途ごとに複数立てたいという要求もあり、Dockerを使えばコンテナイメージを環境変数etcをかえて使い回しすればいいので、そういう意味でもDockerは役に立った。

たとえば、普通にJenkinsをYUMやらで入れたとして、これを複数インスタンス立ち上げるのはちょっと苦痛だろう。

そういう場合はDockerを使って、Jenkinsのイメージを使いまわせばいいのである。細かい要件はJenkinsスレーブ(こちらもDockerイメージ)に吸収させれば少なくともJenkinsマスタのイメージは複数インスタンスで使いまわせる。

Dockerfileは多くの開発者が慣れ親しんでいるCentOSベースに、ある程度万能な状態のコンテナを派生させて色々作ったが、今思えばもっとフラットに構成すればよかったと思っている。結局、万能層なんて置くと外から見て何がどうなっているのかわかりづらい。

docker pullして済むようなものについては、特に自分でイメージもつくらずに使っている。ただ、そんなことができるのはレアケースで、大概本格的に使うなら、自分でケツを拭けるコンテナじゃないと行き詰まることが多いはずだ。

既存のLinuxソフトウェアはDockerなんぞ知らんという作りになっているので、うまくイメージに落とせないことも多いし、開発用メールサーバとしてpostfixdovecotをコンテナに閉じ込めるのは私にとっては非常に苦痛だった。

この類の苦痛の回避策として、アプリをうまく事前準備なしで初期化動作をさせようとすると、コンテナに起動用シェルが待ち構えて本丸を最後にforkかexecかするケースが多い気がする。

タイムゾーンとかズレたり、好きなDNS見させるのに苦労したり、ログの吐き場所とか苦労したりなんか色々苦労した。

そもそもDockerを扱っていて、これLinuxの知識割りと要るよねと思うこと多数。今Dockerを自由自在につかえている人はLinuxに相当詳しいはず。

まあこういう雑魚が引っかかる問題はバージョン上がったら古い人しか知らないアホみたいな問題になっているのかもしれないけど。

それとプロキシを噛ませないといけない環境だと死ぬほど苦労した。

コンテナのyumやaptがそのままじゃプロキシ見ないので、じゃあ、どうするかっていう…。Dockerのデーモン自体がプロキシ見るのは簡単なんですけどね。プロキシの設定がコンテナに残るとか気持ち悪すぎる。

そもそもプロキシがなくてもビルド中のインターネットアクセスが割りと苦痛だったりする。キャッシュプロキシは不可避だろうか。

Dockerとは何か?

Docker初心者が口にする最も多い質問だと思う。

Linux専門の)VMの1種というと分かりやすいけど、違う。

(ただ、初心者にとってはSSHDをDockerで立てれば、それがたぶんいっぱしのサーバに見えてくる的な感覚?はあると思う。)

Dockerの哲学をあらわすのはアプリケーションコンテナという言葉だと思う。

たとえばJavaで何かのアプリケーションを作っていたとして、それがどんなJREで動くのか非常に曖昧だったり、どういうディレクトリ構造が実際にあるのか曖昧だったりで、やる気のある人はサーバ環境をChefとかで構成してみるものの、やや実環境の構成の不明瞭さに一抹の不安感が漂ったりすると思う。

そういった場合に、アプリケーションがどこかのディレクトリにインストールされ、どこかの/usr/binにあるバイナリが存在するのを期待するという受け身のアプリという姿勢から、1段階引き上げて、OSの環境ごとアプリを封じ込めて、それをそのままいろんな環境に持っていければ、あまり実環境の構成の不明瞭さを気にしなくてもよくなる。

これがDockerのアプリケーションコンテナという考え方であると思うし、Dockerfileもそれに準拠する形で作れば、そうそう狂ったものにならない、と思っている。

ただ、Dockerはあくまで道具だし、綺麗にならないものもいっぱいあるし、時間は有限だし、私イジけちゃうしってことで、まあただのアプリの檻として好きに使っても後ろ指さされることはないと思います。

Dockerの檻としてのセキュリティはどうなんですかね~。Dockerチームは確かそういうのにケアしているとか言っていたような気が1mmぐらいはしますが、セキュリティ確保は副次的なものだと思ってます。

信用できないコンテナとかは動かせないんじゃないんでしょうか。

Dockerで個人的に消化不良起こしているもの

  • Ambassadorパターン
  • supervisordという存在
  • ロールベース

Dockerで個人的に期待すること

AntでEclipseのECJ(Eclipse Compiler for Java)を使う

はじめに

知っている人にはよく知られていることですが、EclipseJava開発環境、JDTがJavaソースのコンパイルに使っているコンパイラは、OracleJDKなどとは違い、Eclipseが独自実装しているコンパイラ ECJ (Eclipse Compiler for Java)です。

ECJとOracle JDK(OpenJDK)のjavacと何が違うのかというと、

  • コンパイラの許容ルールが異なる。(おそらくOracle JDKより緩いケースが多そう)
    • これのせいでEclipseコンパイルできたとしても、javacでコンパイルできないソースが出るので、そういうのやめたい人はきちんとJenkinsとかで常時チェックかかるようにしましょう。
  • コンパイルが一部失敗しても成功したclassファイルは生成される。
    • それどころか失敗したクラスでさえ仮のclassファイルを生成させることができる。
    • ある程度のコンパイルエラーを許容しなければならない荒っぽいプロジェクトでは使える性質でしょう。
  • コンパイラオプションが異なる。
  • 警告などの出力フォーマットが異なる。
    • JenkinsでWarnings Pluginを使っている場合は、ECJ用のルールがデフォルトで存在しています。

ECJはEclipseを立ちあげなくてもAntから使えます。(batch compiler)

んじゃあ、AntでEclipseJavaソースコンパイラ使ってみよう

プレーンなbuild.xml

たとえば、下記のようなフォルダ構成だったとします。

project-root/
  build.xml
  src/
    Hoge.java
  compiled/
    (Hoge.classがここにできればいいな)

この場合、build.xmlはこうなるでしょう。(わざとシンプルにしてます。Ant1.8以上)

<project default="build">
       <target name="build">
             <delete dir="compiled" />
             <mkdir dir="compiled" />
             <javac srcdir="src" destdir="compiled" />
       </target>
</project>

上記例だとパスの通ってるjavacを叩いているので、これをECJに切り替えましょう。

ECJを手に入れよう

ECJを構成するJARを手に入れましょう。

入手方法1. 直接ダウンロード

下記のMavenリポジトリ

http://mvnrepository.com/artifact/org.eclipse.jdt.core.compiler/ecj/

で見つかりました。 (groupId=org.eclipse.jdt.core.compiler, artifactId=ecj)

どっか公式DLページあるんですかね?(検索あきらめた)

ecj-..jar をダウンロードしましょう。JARの中にAnt用のアダプタとコンパイラコア本体が入っています。

ここでは project-root/ant-lib/ にそのjarをおいておきます。

入手方法2. Eclipseから取得

Eclipse本体からでも取得できます。

まずは下記の場所からJDTのコアのプラグインJARを見つけましょう。

eclipseインストールパス/plugins/
  org.eclipse.jdt.core_3.10.0.v20140604-1726.jar みたいなの

んで、上記jarをzipファイルとして解凍してAntへのアダプタとなる jdtCompilerAdapter.jar をとっておきましょう。

ここでは下記2つのJARを project-root/ant-lib/ にいれておきます。

  • org.eclipse.jdt.core_3.10.0.v20140604-1726.jar
  • jdtCompilerAdapter.jar

Ant関係なくECJを起動してみよう

ECJはスタンドアロンでjavacのように扱うことが可能です。javacと基本ほとんど同じですね。

java -jar ECJJARファイル コンパイルしたいソース.java

※ECJJARファイルはそれぞれ下記を使用。
 入手方法1の場合はecj-*.*.jar
 入手方法2の場合はorg.eclipse.jdt.core_3.10.0.v20140604-1726.jar
※マニフェストで起動したくない場合は起動クラスとして org.eclipse.jdt.internal.compiler.batch.Main を指定すればよい。
※依存するライブラリがある場合は-classpath "依存.jar"みたいな感じでjavaに渡してあげる。
※細かいオプションみたい人は -? 引数を指定してみてね。

ECJをbuild.xmlに組み込んでAntから呼んでみよう

ECJをtypedefし、それをjavacで参照、javacのcompilerでorg.eclipse.jdt.core.JDTCompilerAdapterを使うように指定すればOKです。

<project default="build">
     <typedef name="ecj" classname="org.eclipse.jdt.core.JDTCompilerAdapter">
          <classpath>
               <fileset dir="ant-lib" includes="*.jar" />
          </classpath>
     </typedef>
     <target name="build">
          <delete dir="compiled" />
          <mkdir dir="compiled" />
          <javac srcdir="src" destdir="compiled" compiler="org.eclipse.jdt.core.JDTCompilerAdapter">
               <ecj />
          </javac>
     </target>
</project>

typedef+<ecj />が面倒な人は、下記のいずれかのようにすれば、compiler属性の指定だけで使えます。

  • Ant起動時に-lib ECJのjarを指定する。
  • ${ANT_HOME}/lib~/.ant/lib にそのままECJ関連のjar放り込む。
  • typedefを作らず下記のようにcompilerclasspathをインラインで書く。
          <javac srcdir="src" destdir="compiled">
               <compilerclasspath>
                    <fileset dir="ant-lib" includes="*.jar" />
               </compilerclasspath>
          </javac>

compiler属性の指定が面倒な人は、下記のいずれかをすればいいでしょう。

  • <property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter" />をどこかに入れておいたり、
  • Antの起動引数でプロパティ指定しておけば

まあ、macrodefとか使ってもいいと思うし、お好きに。

動作させてみよう

さっそくこれを動かしてみると、(わざとエラー出させています)

Buildfile: F:\eclipse-luna\workspace\CompileWithEclipseCompiler\build.xml
build :
   [ delete] Deleting directory F:\eclipse-luna\workspace\CompileWithEclipseCompiler\compiled
    [ mkdir ] Created dir: F:\eclipse-luna\workspace\CompileWithEclipseCompiler\compiled
    [ javac ] F:\eclipse-luna\workspace\CompileWithEclipseCompiler\build.xml:6: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [ javac ] Compiling 1 source file to F:\eclipse-luna\workspace\CompileWithEclipseCompiler\compiled
    [ javac ] ----------
    [ javac ] 1. ERROR in F:\eclipse-luna\workspace\CompileWithEclipseCompiler\src\Hoge.java (at  line 5)
    [ javac ]       String missing_semicolon
    [ javac ]              ^^^^^^^^^^^^^^^^^
    [ javac ] Syntax error, insert ";" to complete BlockStatements
    [ javac ] ----------
    [ javac ] 1 problem (1 error)

BUILD FAILED
F:\eclipse-luna\workspace\CompileWithEclipseCompiler\build.xml:6: Compile failed; see the compiler error output for details.

Total time: 752 milliseconds

ちょっと出力ログ形式がOracle JDK(下記)と違いますね。

    [ javac ] F:\eclipse-luna\workspace\CompileWithEclipseCompiler\src\Hoge.java :5: エラー: ';'がありません
    [ javac ]             String missing_semicolon
    [ javac ]                                     ^
    [ javac ] エラー1個

もちろんコンパイルが成功した場合はclassファイルがcompiledフォルダに出力されます。

独自オプション

コンパイラのオプションは http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-using_batch_compiler.htm とかにまとまっています。

たとえば-proceedOnErrorというのを下記のように指定すれば、(compilearg要素@line属性)

          <javac srcdir="src" destdir="compiled" compiler="org.eclipse.jdt.core.JDTCompilerAdapter">
               <ecj />
               <compilerarg line="-proceedOnError" />
          </javac>

エラーがでているクラスでも仮のclassファイルを生成してくれます。

もちろんコンパイルエラーが発生しているメソッドを呼んだりすると Exception in thread "main" java.lang.Error: Unresolved compilation problem: などと怒られます。

まとめ

Eclipseコンパイラを使うことでしか動作できないプロジェクト作るのやめてほしいです…(切実)

試しにDockerリポジトリにイメージ(knjname/jenkins)登録してみた

試しにDockerリポジトリに自作イメージ登録してみました。

無料アカウントだと1つまでプライベートDockerリポジトリを持てるみたいです。

今回はknjname/jenkinsというDockerイメージを公開リポジトリにpushしてみました。

SCM : https://github.com/knjname/docker-jenkins

Dockerレポジトリ : https://registry.hub.docker.com/u/knjname/jenkins/

登録する時はあらかじめリポジトリを作っておいて(今回はknjname/jenkinsとして作成)、下記のようにすれば勝手に作成したリポジトリに収まります。

docker push knjname/jenkins
# この後アカウント情報訊かれる

使い方は簡単。

docker run --name testjenkins -d -p 8080:8080 knjname/jenkins

http://localhost:8080 にアクセスすればJenkinsが立ち上がっているのが見えるはずです。

(2014-06-13 追記)なお、このあとGithubと連携するAutomated Buildを試すためにリポジトリを作りなおしたところ、未だにビルドがされていない模様…。

(2014-06-14 追記)ようやくビルドされました!なんかDockerhub自体はまだまだ不安定なところがあるかもしれませんね。