mike-neckのブログ

Java or Groovy or Swift or Golang

Swift 用の正規表現ライブラリー

これは Qiita のアドベントカレンダー2018 の19日目のエントリーです。

qiita.com

Swift の勉強を始めたのが今年の5月末からで、約半年程度 Swift をやってきました。ただ、僕は仕事では一切 Swift を書いていないので、知見に溢れた他の方のエントリーを楽しみにしています(突然の煽り)。

f:id:mike_neck:20180609045321p:plain


Swift を勉強し始めて3ヶ月くらいの頃に、ふとしたきっかけで正規表現が欲しくなりました。調べてみたところどうやら Swift 単体で使える正規表現がなく、 NSRegularExpression を使って、結構面倒そうなコードを書くらしいということがわかりました。しかし、これが Linux で使えるのかわからない(実際には使える)ので、その時は正規表現を使わない方法でとりあえず回避しました。

また、後日、詳しそうな人に尋ねたところ、 NSRegularExpression をラップして String などの extension とするライブラリーを自作しているとのことでした。 実際、 GitHub で 「 Swift regex 」 で調べてみると、そのようなライブラリーがたくさんあるようです。


というわけで、そのようなライブラリーを使えばよいのですが、 Swift の勉強がてら正規表現ライブラリーを作ってみるのも面白そうだと思い、作ってみることにしました。レポジトリーはこちらです。

github.com


仕組み

仕組みはいたって簡単で、 C 言語の正規表現ライブラリーをラップしているだけです。今のところ POSIX正規表現(regex.h)をラップしたもののみを提供しています。Swift から C 言語を呼び出す方法等は過去にエントリーを書いているので、そちらを見ると良いと思います。

mike-neck.hatenadiary.com

mike-neck.hatenadiary.com


導入方法

Swift Package Manager を使います。残念ながらその他のビルドシステムの方法は知らないので、PRくれるとノールックでマージすると思います。

次の依存ライブラリーを追加します。

  dependencies [
    .package(url: "https://github.com/mike-neck/swift-regex.git", from: "0.1"),
  ]

ターゲットに加えます。

targets [
    .target(name: "YourAwesomeApp", dependencies: [
        "SwiftRegex",
        "POSIXRegex",
    ]),
]

このライブラリーには今のところ3つモジュールがあります。

  • SwiftRegex - API を定めているモジュール
  • POSIXRegex - APIPOSIX 実装モジュールで、次の CPOSIXRegex モジュールをラップしています
  • CPOSIXRegex - POSIX正規表現を呼び出す C で書かれたモジュール

これらのうち、 API と 実装モジュールをターゲットに加えれば、このライブラリーを利用できます。


コード例

import POSIXRegex
import SwiftRegex

SwiftRegexImplementation.by.usePosix() // 1

guard let regex = pattern(of: "ba").compile() else { // 2
    fatalError { "cannot compile pattern \"ba\"" }
} 

let matcher = regex.matcher(for: "foo-bar-baz") //3
matcher.matches // -> true //4
  1. SwiftRegexSwiftRegexImplementation という長ったらしい名前の構造体があり、それの static プロパティ byusePosix() という関数が生えているので、これを呼び出すと、 POSIX 実装の正規表現が使えます。
  2. pattern(of:) という関数があるので、それを呼び出してからコンパイルします。コンパイルの結果は SwiftRegex? となっており、正規表現が間違えているなどコンパイルできない場合は nil が返ってきます。
  3. 検索ソースの文字列を matcher(for:) 関数にわたすと検索結果が返ってきます
  4. 今のところ matches プロパティで検索結果だけが取り出せるようになっています。(今後、置換を実装していこうと思っています)

という感じのライブラリーですが、どうやら Swift5 以降に正規表現が加わるかもしれないそうなので短命に終わるかもしれないです。「へぇ〜」と思ったそこのアナタ!、ぜひGitHubでスターを付けてってください!【2018/12/19 15:45 間違えているので修正しました】

JJUG CCC 2018 (Winterでなく)Fall に参加してきた

表記の通り、 JJUG CCC 2018 (もう 12 月だけど) Fall に参加してきた

www.java-users.jp

で、恒例のそのメモ

f:id:mike_neck:20181019112748p:plain


Pivotal認定講師によるSpring Framework 5.1ハンズオン!

日本で唯一の Pivotal 認定講師カサレアルの 多田さん とカサレアルのスタッフによる Spring Framework 5.1 のハンズオン。 (【2018/12/17 9:08 追記】後ほど教えてもらった話によると、正確には多田さんは Spring Cloud と Cloud Foundry の唯一の認定講師で、 Spring の認定講師には 土岐さんもいらっしゃるとのこと)

教材がかなり作り込まれており、ひょっとしたら、カサレアルで行っている研修内容に近いのではないかと思えるくらいクオリティの高いハンズオンでした(カサレアルのページによると、 『はじめての Spring Mvc によるシステム開発』 というコースで 10 万円だそうで、このセッションに参加するだけで5万円くらいはとってもよさそうなくらいのクオリティ)。

また、多田さんは日本で唯一の Pivotal 認定講師ですが、その一方で、「アブストラクアノテーションコンフィグディスパッチャーサーブレットイニシャライザー」を早口で舌を噛まずに言える日本人唯一の人なので、それを聞くためにも参加して損はしないハンズオンでした。

参加した意図は Spring にここ最近触れていないので、感覚を取り戻しておきたかったからです。ほとんど変わっていないことは確認できる一方、 @RepositoryCrudRepository につけなくてもよくなっていたりで、より簡略できるようになっていることが確認できました。

あとどうでも良い話ですが、前日に準備してくださいと言われていたので、準備した際に、宗教的な理由により maven で構築されたプロジェクトを gradle に移行して本セッションに臨みました。


思考停止しないアーキテクチャー設計

無職を辞めて最近会社を作った 川島さん のセッション。昼休みから戻った時点で部屋が6〜7割埋まってしまうほどの大人気セッションでした。

雑にまとめた内容

  • 非機能要件からアーキテクチャーを決定していく
  • ただし機能要件からもアーキテクチャー要求が発生することがある
  • どんなに丁寧にやってもアーキテクチャー設計の考慮漏れを防ぐのは難しい
  • 企画した後に前提が変わることもあり、技術的負債のメモ/金額換算をしておいて、リスク等を低減する

なお、冒頭に会社設立のゲーム化したもののデモプレイが流れました。 URL を取り忘れたので、誰かあとで教えてください。

(【2018/12/17 9:08 追記】起業クエスト はこちら → 起業クエスト tada_suzu さん ありがとうございます)


複雑なドメインに泥臭く立ち向かう

Koji Sudoさんによる 複雑な業務をどのようにモデル化して、実装に落とし込むかの理論的な話と実際の取り組み例を紹介するセッションでした。すごく話し上手で、聞いているだけで自分があたかもデキる人に感じてしまう魅惑なセッションでもあります。

以下雑なまとめ

  • アプリケーションの最も実現したい部分をまず作ってから、複合的なパターンを構築していく
    • 複雑なものを最初からすべて実現するように作ると、複雑で読解困難なものができてしまう
  • 複雑な業務をモデル化するために
    • 業務について書かれている書籍やガイドなどをもとに勉強する
    • 業務を実際に体験する(ロールプレイングする)
  • モデルを抽出するのは、複雑なものを簡単にするためではなく、複雑なものを人間が理解できるようにするため
  • 複数人で開発する場合、分野により個々の力量差があるため、モブプログラミングを採用するなどして複数のプログラムにレベルの差が現れないようにする
  • データと情報を区別する
    • データ : クラスを構成する値など
    • 情報 : データから導き出される値など

次の時間は眠気で休んでいました

しかし、久保田さんのセッションと思しきツイートがいくつか見られました。自分の勤務先でも現在 Java8 から Java 11 への移行を計画していますが、一度フレームワーク/ライブラリー等の移行のための動作確認等を行っておいたほうがよいかと思いました。


コードをど真ん中にそえた設計アプローチ

いろふさん のセッション。上級者向けとあり、世の中の Java 上級エンジニアが大挙して集まるセッションでした。内容はコードとドキュメントと設計をどのようにして楽におこなうかという話。

以下雑なまとめ

  • ドキュメントは基本的にメンテされないので、動くコードから生成する
  • ロジックよりは型に意図を込めていく
  • ドキュメントとするためにコードが歪むのは本末転倒

また、 JIG というライブラリーを作っていて、こちらからドキュメントを生成するような仕組みを作っているとのことです。

github.com


アンカンファレンス「OSSとコミュニティの運営など」

本当は 「GCを発生させないJVMとコーディングスタイル」 を見ようと思っていたのですが、長蛇の列を見て諦めました。で、とりあえず入ったのがこのセッション。

  • OSS のフリーライド等
    • US の企業で OSS を無償(ノーサポート)で使っていてそのトラブルに見舞われて解決できない場合などは、その企業は株主にかなり責め立てられるそうです(当然、株は売られるので株価が下がる)。したがって、 OSS を使う場合にお金を払うのは企業の姿として至極当然のようです(Red Hat はこのあたりをビジネスにしている)。
  • Meetup のドタキャン
    • 日本だけの問題でもないとのこと
    • わりと企業が Meetup のスポンサーになるそうで、懇親会をしてから本編をやるなどの工夫があるようです

懇親会

本来は懇親会に参加の予定はなかったのですが、 Java チャンピオンの せろさん の機転により参加できました。


今回振り返ってみてですが、あまり先進的なトピックのセッションではなく、開発プロセスの話を多く聞いたように思います。開発プロセスの話に面白そうなのが多かったというのが理由です。

Vim でも AWS lambda 関数

普段 vimh/j/k/l/d/x/0/$/:wq くらいしか使わないのですが、ひょんなことから Vim で Web サーバーがたてられると聞いたので、サーバーが建てられるなら、 http クライアントも動くのだろうと考えて、 ラムダで動くかどうか試してみることにしました。

f:id:mike_neck:20181208195209p:plain


使用する vim

おそらく lambda を動かしている vm には、必要がないから vim は入ってないだろうと考え、 vim をビルドします。なお、このビルドは Amazon Linux 2 で実行しています。

yum install -y git ncurses-devel 
yum groupinstall "Development Tools"
git clone https://github.com/vim/vim
cd vim
./configure --with-features=normal --prefix=$HOME/release

vim の設定はなるべく軽いものにしたかったので、 --with-features オプションは normal にしました。また、リリースするのが目的なので、 --prefix$HOME の下の release ディレクトリーにしました。

また、試しに実行したときに libtinfo.so.6 がないため起動できなかったので、これを release ディレクトリーの下の lib ディレクトリーにコピーします。

このビルドが終わると、 release ディレクトリーの下はこのような形になります。

release
├── bin
│   └── vim
├── bootstrap
└── lib
    └── libtinfo.so.6

vim スクリプト

最初 bootstrap は単なる bash スクリプトにしようかと思いましたが、極力 vim でやろうと思ったのと、 shebang をつけてうまく終わるようにスクリプトを書けば、 vim の画面を表示しないでも vim を実行できる見通しがたったので、 bootstrapvim スクリプトを書いています。ちなみに vim スクリプトを書くのはこれが初めてです。

#!bin/vim -u

:let header = system("mktemp")[:-2]
:let request = "http://" . $AWS_LAMBDA_RUNTIME_API . "/2018-06-01/runtime/invocation/next"

:while 1
:  let event = system("curl -L -D " . header . " " . request)
:  let payload = "{\"receive\":" . event . "}"

:  let headers = readfile(header)
:  let id = ""
:  for item in headers
:    if item =~ "Lambda-Runtime-Aws-Request-Id"
:      let id = substitute(item, "Lambda-Runtime-Aws-Request-Id", "", "g")
:      let id = substitute(id, ":", "", "g")
:      let id = substitute(id, " ", "", "g")
:    endif
:  endfor

:  echo "==request=="
:  echo event
:  echo "==id=="
:  echo id

:  let response = "http://" . $AWS_LAMBDA_RUNTIME_API . "/2018-06-01/runtime/invocation/" . id . "/response"
:  echo response
:  let result = system("curl " . response . " -d '" . payload . "'")
:endwhile

:exit

リリース/デプロイ

先程のディレクトリーの構造

release
├── bin
│   └── vim
├── bootstrap
└── lib
    └── libtinfo.so.6

これを維持したまま、 zip でアーカイブして、 lambda にリリースします。


実行

lambda のコンソールから実行してみました。

次のようなペイロードを投げると、 receive というプロパティに同じオブジェクトが入った json が返ってくるはずです。

テストイベント

{
  "editor": ["vim","emacs","nano"],
  "language": "vimrc"
}

期待するレスポンス

{
  "receive": {
    "editor": ["vim","emacs","nano"],
    "language": "vimrc"
  }
}

そして実行結果の画面キャプチャーはこちら

f:id:mike_neck:20181208195011p:plain
テスト実行


というわけで、 AWS Lambda のカスタムランタイムとして vim を走らせることができました。 Vim スクリプトは サーバーレスアプリケーションを記述する言語になりうるし、 Vim はサーバーレスアプリケーション用のフレームワークと言うことができる。

JEP-330 Single-FIle Source-Code Programs って何? Java ファイルが java コマンドで実行できるってホント?! shebang でどう指定するの?調べてみた!

Java11 で取り込まれている JEP-330 すでにきしださんがブログにしている古い機能ですが、自分用のメモ。

f:id:mike_neck:20181019112748p:plain

(『古い機能』という表現はその手の界隈に人達への忖度表現)


TL;DR ってなんだ???

  • Java 11 にとりこまれた JEP-330 は 1つの java ファイルを java コマンドで実行できる仕組み
  • java という拡張子でなければ、ファイルの先頭に shebang をつけて実行可能ファイルとして実行できる
  • 内部的には javac に依存しているので、カスタムランタイムなどでは実行できない
  • 外部の jar も利用できる
  • shebangclasspath の指定をする場合は順番に要注意

JEP-330(Launch Single-File Source-Code Programs)

JEP-330 では一つのソースコードファイルだけであれば、 java コマンドで実行できるようにしてしまう機能です。

JEP 330: Launch Single-File Source-Code Programs

d.hatena.ne.jp

物は試しに次のようなファイル Hello.java を作ります。

class Hello {
  public static void main(String... args) { System.out.println("Hello"); }
}

このファイルに対して、次のコマンドを実行します。

java Hello.java

結果のスクショをどうぞ。

f:id:mike_neck:20181208130230p:plain
java ファイルを実行できる

ちなみに、上記コマンドの正式な実行の仕方は以下の通りだそうです。

java --source 11 Hello.java

もちろん、この 11Java 11 の 11 で、 11 よりもやや小さい値でも実行できるようです。

f:id:mike_neck:20181208131051p:plain
--source 8

6 を指定すると警告が大量に出ますが、実行できるようです。

f:id:mike_neck:20181208131206p:plain
6 の場合は警告が大量に…

サポートの終わっている 5 では残念ながら実行できません。

f:id:mike_neck:20181208131324p:plain
5 では実行できない


shebang のついてる java

java コマンドで java ファイルを一発で実行できるということは、 java ファイルを解釈するインタプリターとして java コマンドが使えるということなので、次のようなファイルでも valid な java ファイルと言えます。そこで、先程の Hello.java の先頭行に shebang をつけます。

#!/usr/bin/env java --source 11
class Hello {
  public static void main(String... args) { System.out.println("Hello"); }
}

さらに、実行可能にします。

chmod +x Hello.java

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

f:id:mike_neck:20181208131706p:plain
失敗した?!

どうやら、 先程のような実行するスクリプトとは認識せずに、 コンパイルするファイルとして認識されてしまった模様です。

このコードの名前を少し変えてみましょう。

mv Hello.java hello
./hello

実行結果はこちらです。

f:id:mike_neck:20181208132114p:plain
実行できた!

どうやら、拡張子が .java になっていると実行ファイルとみなされないようです。


java コマンドだけあればいい?

ここで気になるのが、 この機能は java コマンドだけあればいけるのかということです。たとえば、前回のエントリーのようにカスタムランタイムを作った場合に、実行する内容がシングルファイルで書ける内容であればコンパイルせずに lambda に乗せて実行できるのでしょうか?

mike-neck.hatenadiary.com

試してみます。次のコマンドで作ったカスタムランタイムを shebang で指定してみます。

jlink \
  --compress=2 \
  --module-path $JAVA_HOME/mods \
  --add-modules java.base,java.net.http \
  --output runtime

f:id:mike_neck:20181208134421p:plain
実行できませんでした!

先程のツイートにきつねくんから回答をもらっていて、内部的には javac を実行しているとのことです。 java ファイルとカスタムランタイムをリリースして Java で Script なで JavaS(以下自粛) という淡い夢は儚くも散りました。


ライブラリー使えるの?

次に気になるのが、ライブラリーがある場合はどうなのでしょう?

これも試してみましょう。

ここでは AssertJ を使ってみます。

先程のコードも次のように書き直します。 falseisTrue なわけがないので、落ちるテストです。

import static org.assertj.core.api.Assertions.*;
public class Assert {
  public static void main(String... args) {
    assertThat(false).isTrue();
  }
}

次のコマンドで実行します。

java \
  --class-path assertj-core-3.9.1.jar \
  Assert.java

実行結果はこちら。

f:id:mike_neck:20181208140023p:plain
外部の jar も取り込める


shebang の中で classpath 指定したい

さらにもう一歩進んで、 shebang の中で jar ファイルを指定してみましょう。先程の Assert.javaassert に改名して、 shebang をつけます。

#!/usr/bin/env java --class-path assertj-core-3.9.1.jar --source 11 
import static org.assertj.core.api.Assertions.*;
public class Assert {
  public static void main(String... args) {
    assertThat(false).isTrue();
  }
}

こちらを実行したらどうなるでしょうか?

f:id:mike_neck:20181208140845p:plain
shebang の中で classpath を指定する

うまく実行できるようです。なお、 shebang の中での --class-path--source の順番を入れ替えてみました。すると…

f:id:mike_neck:20181208141142p:plain
shebang に書く順番を間違えると実行できない

実行できなくなるようです。


JEP-330 の Single-File Source-Code Programs について紹介してきましたがいかがでしたでしょうか。

これまでちょっとしたアプリケーションも javac でコンパイルしなければならなかったので、ずいぶんと便利になりましたね。

もし何かぬけもれがあるようでしたら、ぜひ教えてくださいね。

今回も最後までお読みいただきありがとうございました。