2015年6月28日日曜日

アルゴリズムプログラム 積

アルゴリズムをプログラムする方法を学ぶ。

積を計算することは、現代では0位取り表記によって大変容易になったが、古代エジプトなどでは専門性を求められるものであった。

積は和を複数回繰り返すことによって実現できる。コンピュータではビット演算を組み合わせることによってより効率的に積を計算することができる。

以下は、アーメスのアルゴリズムとその改善版を記述したコードである。
コードが最適化されているか常に思考し、よりよいコードに変換する作業を繰り返すことが大切だとわかる。


//
// multiplication.cpp
// CplusplusPractice
//
// Created by masai on 2015/06/27.
// Copyright (c) 2015年 masai. All rights reserved.
//
#include <iostream>
// xの最上位ビットを評価する(奇数判定する)
bool odd(int n){ return n & 0x1; }
// xを右へ1つシフトする(1/2する)
int half(int n){ return n >> 1; }
// 乗法1 a + a + ... + a
int multiply0(int n, int a){
if(n == 1) return a;
return multiply0(n - 1, a) + a;
}
// 乗法2 a + a + a .. + a = (a + a) + (a + a) + ... + a
int multiply1(int n, int a){
if (n == 1) return a;
// a + a + a + a = (a + a) + (a + a)
int result = multiply1(half(n), a + a);
// (a + a) + (a + a) + (a + a) (nの2進数表現における1の数(population count) - 1 だけ呼ばれる)
if(odd(n)){
std::cout << "plus" << std::endl;
result = result + a;
}
return result;
}
// おまけ: 16進数表現
void output16(int a){
printf("a = 0x%x", a);
}
// 15倍の加法連鎖
int multiply_by_15(int a){
int b = (a + a) + a;
int c = b + b;
return (c + c) + b;
}
// 乗累算1(関数呼び出しを減らす)
int multi_acc0(int r, int n, int a){
if(n == 1) return r + a;
if(odd(n)){
return multi_acc0(r + a, half(n), a + a);
}else{
return multi_acc0(r, half(n), a + a);
}
}
// 乗累算2(記述を単純化する)このような状態を末尾再帰(tail-recursive)という
int multi_acc1(int r, int n, int a){
if(n ==1)return r + a;
if(odd(n)) r = r + a;
return multi_acc1(r, half(n), a + a);
}
// 乗累算3 比較回数の削減(比較の順序によって計算回数が減るケースがある)
int multi_acc2(int r, int n, int a){
if(odd(n)){
r = r + a;
if(n ==1)return r;
}
return multi_acc2(r, half(n), a + a);
}
// 乗累算4 正確なtail-recursive → 再帰から繰り返し構造に置き換えることが簡単
int multi_acc3(int r, int n, int a){
if(odd(n)){
r = r + a;
if(n ==1)return r;
}
n = half(n);
a = a + a;
return multi_acc3(r, n, a);
}
// 乗累算5 再帰から繰り返し構造への置き換え
int multi_acc4(int r, int n, int a){
while(true)
{
if(odd(n)){
r = r + a;
if(n ==1)return r;
}
n = half(n);
a = a + a;
}
}
// ヘルパー1
int multiply2(int n, int a){
if(n == 1) return a;
return multi_acc4(a, n - 1, a);
}
// ヘルパー2
int multiply3(int n, int a){
// nが奇数になるまで、1/2を繰り返す
while(!odd(n)){
a = a + a;
n = half(n);
}
return multiply2(n, a);
}
// ヘルパー3
int multiply4(int n, int a){
// nが奇数になるまで、1/2を繰り返す
while(!odd(n)){
a = a + a;
n = half(n);
}
if(n == 1) return a;
// かならずnが奇数で呼び出すので奇数のときに行われる処理を事前に行っておく
return multi_acc4(a, half(n - 1), a + a);
}
int main(int argc, char* argv[]){
std::cout << "multiplication." << std::endl;
// 和の再帰で積を表現
std::cout << "multipliycation = add." << std::endl;
std::cout << multiply0(3, 2) << std::endl;
// 奇数
std::cout << "odd" << std::endl;
std::cout << odd(13) << std::endl;
// 偶数
std::cout << "even" << std::endl;
std::cout << odd(12) << std::endl;
// 1/2
std::cout << "1/2" << std::endl;
std::cout << half(5) << std::endl;
// 積(アーメスのアルゴリズム)
std::cout << "egyption multiplycation." << std::endl;
std::cout << multiply1(6, 2) << std::endl;
// おまけ: 16進表現
output16(100);
// 15の加法連鎖
std::cout << "multipy by 15." << std::endl;
std::cout << multiply_by_15(30) << std::endl;
// 乗累算
std::cout << multi_acc1(0, 5, 100) << std::endl;
std::cout << "optimize multipy." << std::endl;
std::cout << multi_acc2(0, 5, 100) << std::endl;
std::cout << multi_acc3(0, 5, 100) << std::endl;
std::cout << multi_acc4(0, 5, 100) << std::endl;
// ヘルパー呼び出し
std::cout << multiply2(5, 100) << std::endl;
std::cout << multiply3(5, 100) << std::endl;
std::cout << multiply4(5, 100) << std::endl;
return 0;
}

2015年6月21日日曜日

vagrantとansibleを使ってdocker開発環境をつくる

vagrantは開発環境用のVM構築と共有を簡単に行うためのVMラッパーツールであるが、ansibleを利用して開発環境の設定(プロビジョニング)も繰り返しを避けることができる。

vagrantは、プロビジョニング用の設定をVagrantfileに記述することが可能である。
今回は、プロビジョニングにansibleを利用する。ansibleはplaybookと呼ばれるyaml形式の設定ファイルを参照する。

Vagrantfileには以下のように追記する。ansibleはplaybook.ymlを参照する。

config.vm.provision "ansible" do |ansible|
ansible.playbook = "playbook.yml"
end
view raw Vagrangfile.txt hosted with ❤ by GitHub
ansibleに使用するplaybookを作成する。

---
- hosts: all
tasks:
- name: install latest docker
shell: curl -sSL https://get.docker.com/ubuntu/ | sudo sh
- name: add vagrant user to docker group
command: gpasswd -a vagrant docker
sudo: yes
view raw playbook.yml hosted with ❤ by GitHub
Vagrantfileと同じディレクトリにplaybook.ymlを入れておく。
vagrant up で playbookを利用してプロビジョニングできる。既に vagrant up 済みのVMは以下のように明示的にプロビジョニングする必要がある。

#vagrant provision [~/vagrant/ubunt1404]
==> default: Running provisioner: ansible...
PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --private-key=/Users/masai/vagrant/ubunt1404/.vagrant/machines/default/virtualbox/private_key --user=vagrant --connection=ssh --limit='default' --inventory-file=/Users/masai/vagrant/ubunt1404/.vagrant/provisioners/ansible/inventory playbook.yml
PLAY [all] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [default]
TASK: [install latest docker] *************************************************
changed: [default]
TASK: [add vagrant user to docker group] **************************************
changed: [default]
PLAY RECAP ********************************************************************
default : ok=3 changed=2 unreachable=0 failed=0

2015年6月19日金曜日

Pythonの軽量WEBフレームワークBottle (WebAPIを手軽に記述)

BottleはPythonの軽量WEBフレームワークである。テスト用に素早くAPIを公開したいなと考えたところ、ピッタリだと思った。

インストール
pipでインストールできる。
pip install bottle

サンプルコード api.py
以下の様な短いコードでWeb APIが動作する。

from bottle import route, run
# アノテーションでURLを記述
@route('/deepapi/json/:name')
def hello(name):
return '<h1>Deep %s!</h1>' % name
# オプションをつけるとデバッグ実行
run(host='masai-no-MacBook-Pro.local', port=8080, debug=True, reloader=True)
#run(host='localhost', port=8080)


起動
python api.py
めちゃくちゃ楽できる・・・。今まで使わなかったことを後悔するくらいだ。

2015年6月17日水曜日

vagrantのboxを使って仮想マシンを起動する

vagrantは追加されたboxの内容にしたがって、以下のコマンドで仮想マシンの起動設定を作成することができる。

# vagrant init <box名>

Vagrantfileが作成されるので、以下のコマンドで起動する。

# vagrant up

起動したVMには以下のコマンドでssh接続できる。

# vagrant ssh

vagrantでWeb開発環境用仮想マシンができるところまでをまとめるつもりである。

vagrantのboxを追加する(vagrantコマンドで直接URLを指定してSSLエラーが出る場合)

vagrantのbox追加で指定するURLはローカルファイルでももちろん可能である。以下は、curlでローカルに保存したboxファイルをvagrantに追加する手順である。

# curl -L https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box > centos.box
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 280M 100 280M 0 0 2068k 0 0:02:18 0:02:18 --:--:-- 4524k
# ls
centos.box
# vagrant box add centos centos.box
==> box: Adding box 'centos' (v0) for provider:
box: Downloading: file:///root/vagrant/centos/centos.box
==> box: Successfully added box 'centos' (v0) for 'virtualbox'!
view raw vagrant_add.sh hosted with ❤ by GitHub

vagrant box list で追加できているか確認できれば完了である。

2015年6月16日火曜日

gvmを利用してgradleをインストールする(OSX)

GVM(Groovy management tool)はGroovy環境構築用ツールである。Groovyの実行バージョンの管理や、Groovyを利用するツールのインストールが可能である。

インスール
curl -s get.gvmtool.net | bash

PATHに追加
export PATH=$PATH:$HOME/.gvm/bin

実行
~/.gvm/bin/gvm-init.sh

gradleのインストール
gvm install gradle

以上で完了。zshを利用している場合には、.zshrcもしくは.zshenvにPATHとコマンド初期化の設定が追加されることを確認すること。

2015年6月12日金曜日

Bing画像検索API Pythonクライアント

学習画像を収集するための、Bing画像API Pythonクライアント。
保存ディレクトリとか検索ワードの指定をパラメータで引き渡すようにして少し使いやすくしたつもり。収集する画像の最大枚数を指定することも可能。

#conding:utf-8
import sys
import os
import urllib
import urllib2
import json
import requests
KEY = '<Your Bing Developer Key>'
OUTPUT = '/images/bing/'
MAX = 100
count = 1
def bing_search(query, directory, skip):
global count
bing_url = 'https://api.datamarket.azure.com/Bing/Search/v1/Image'
print 'search count: ' + str(count) + ', url: ' + bing_url + ', skip: ' + str(skip)
pm = urllib2.HTTPPasswordMgrWithDefaultRealm()
pm.add_password(None, bing_url, KEY, KEY)
handler = urllib2.HTTPBasicAuthHandler(pm)
opener = urllib2.build_opener(handler)
urllib2.install_opener(opener)
if skip > 0:
params = urllib.urlencode({'Query': "'" + query + "'", 'Adult': "'Off'", '$skip': skip ,'$format': 'json'})
else:
params = urllib.urlencode({'Query': "'" + query + "'", 'Adult': "'Off'", '$format': 'json'})
response = urllib2.urlopen(bing_url+'?'+params)
data = json.loads(response.read())
results = data['d']['results']
for item in results:
if count > MAX:
print 'finish. count: ' + str(MAX)
return
image_url = item['MediaUrl']
root,ext = os.path.splitext(image_url)
if ext.lower() == '.jpg':
print image_url,
try:
r = requests.get(image_url)
fname = OUTPUT + directory + "/bing%04d.jpg" % count
f = open(fname, 'wb')
f.write(r.content)
f.close()
print "...save", fname
except:
print "error", fname
count += 1
bing_search(query, directory, count)
if __name__ == '__main__':
argvs = sys.argv
argc = len(argvs)
if(argc != 3):
print 'Usage: python %s query directory' % argvs[0]
quit()
query = argvs[1]
directory = argvs[2]
print 'get bing image: %s ' % query
bing_search(query, directory, 0)

Gemでログを標準出力にする(Ruby)

Gemfileに以下を指定して、rails_stdout_logginライブラリを利用する。
gem 'rails_stdout_logging'

2015年6月11日木曜日

Objective-C メモリ管理

Objective-CSwiftはクラスのインスタンスを参照カウンタで管理する。参照回数が1以上であれば、インスタンスはメモリ上に保持される。

ARC(Automatic Reference Counting)は自動で参照カウンタを管理する仕組みである。retain及びreleaseを明示的な記述を禁止し、自動で行う。

オーナーシップはオブジェクト間の関係性を表すが、強参照と弱参照がある。
強参照は参照先のオブジェクトのオーナーシップを持ち、自分が参照を捨てるまで確実に参照可能である。new(alloc)するとインスタンスへの強参照をもつことになる。

一方、弱参照はオーナーシップをもたず、オブジェクトが破棄されると参照できない。delegateやblocksで用いることが多い。

お互いに強参照の関係にある場合、循環参照と呼ばれる。片方の参照を弱参照にすることで回避できる。


docker ログ出力の設定

dockerでコンテナを作成する際は、ログをコンテナ内に残さないほうが良い。

ログを標準出力に出すように変更する。dockerは標準及びエラー出力されたログをホストの/var/lib/docker/containers/<コンテナID>/<コンテナID>-json.logにJSONファイルとして出力する。

docker logsコマンドで見ることのできるログは、上記のファイルである。Docker Remote APIを介して取得することも可能である。

基本方針

  • コンテナに依存せず、ログを収集する仕組みを準備する
  • ログ収集プロセス自体をコンテナとして動作する

標準出力の設定

各コンテナに割り当てたアプリケーションごとに標準及びエラー出力に設定する。


ログ収集
以下の様なツールを起動するコンテナを準備する。

  • Logspout:dockerコンテナ用のログルータ。Docker Remote APIを介して、同一ホストの全コンテナのログを取得する。LogspoutコンテナからSyslogサーバなどへルーティングすると、複数ホスト上で動くコンテナ郡のログをSyslogサーバで確認することができる。
  • Fluentd:fluentdにはdocker用のプラグインがある。/var/lib/docker/containers以下のログファイルを読み込んで、別のあるファイルへ出力させる。またはS3や集計サービスへ送信する。

ログ集約サービス
Logentries:SaaS型のログ集約サービス



2015年6月10日水曜日

Objective-C 基礎

Objective-Cは触ったことがほとんど無い。基礎からひと通り触る際の記録。

// NSLogの第一引数はフォーマット指定子
NSLog(@"%@", [MixiSampleClass getStaticString]);
// インスタンス生成
MixiSampleClass* obj = [[MixiSampleClass alloc]init];
(void)[obj initWithName:@"abcdefg" sampleType:SampleTypePiyo];
NSLog(@"%@", obj.name);
NSLog(@"%@", obj.name.addSample);
view raw main.m hosted with ❤ by GitHub
Sample.h
//
// MixiSampleClass.h
// TrainingObjectiveC
//
// Created by masai on 2015/06/09.
// Copyright (c) 2015年 masai. All rights reserved.
//
#import <Foundation/Foundation.h>
// 列挙型(SampleType enum)
typedef NS_ENUM(NSInteger, SampleType){
SampleTypeHoge = 0,
SampleTypeHuga,
SampleTypePiyo,
};
@interface MixiSampleClass : NSObject
// プロパティ nonatomic=排他制御しない、strong=オーナーシップをもっている、setter&getterの自動生成
// -setName, -name
@property (nonatomic, strong) NSString *name;
// インスタンスメソッド -
-(id)initWithName:(NSString*) name sampleType:(SampleType) sampleType;
// クラス・メソッド +
+(NSString*) getStaticString;
@end
view raw Sample.h hosted with ❤ by GitHub
Sample.m
//
// MixiSampleClass.m
// TrainingObjectiveC
//
// Created by masai on 2015/06/09.
// Copyright (c) 2015年 masai. All rights reserved.
//
#import "MixiSampleClass.h"
// クラス定数 const
static NSString* const constString = @"const";
// クラス変数 static
static NSString* staticString = @"static";
// 無名カテゴリ(privateなプロパティの拡張、無名クラス無いで宣言したpropertyやメソッドはprivate扱いになる)
@interface MixiSampleClass()
@property (nonatomic, assign) BOOL isEnabled;
@property (nonatomic, assign) SampleType sampleType;
@end
@implementation MixiSampleClass
// インスタンスメソッド -
-(id)initWithName:(NSString*) name sampleType:(SampleType) sampleType
{
self = [super init];
if(self){
// access iVar, self.<var>や_<var>でアクセスが可能
_name = name;
_isEnabled = YES;
_sampleType = sampleType;
}
return self;
}
// クラス・メソッド +
+(NSString*) getStaticString
{
return staticString;
}
@end
view raw Sample.m hosted with ❤ by GitHub

カテゴリ
あるクラスのメソッド郡を別のモジュールとして宣言する。

NSString+SampleAddition.h
//
// NSString+SampleAddition.h
// TrainingObjectiveC
//
// Created by masai on 2015/06/10.
// Copyright (c) 2015年 masai. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSString (SampleAddition)
-(NSString *)addSample;
@end
NSString+SampleAddition.m
//
// NSString+SampleAddition.m
// TrainingObjectiveC
//
// Created by masai on 2015/06/10.
// Copyright (c) 2015年 masai. All rights reserved.
//
#import "NSString+SampleAddition.h"
@implementation NSString (SampleAddition)
-(NSString *)addSample
{
return [NSString stringWithFormat:@"%@sample", self];
}
@end

vagrantによるdocker環境の構築

vagrantとはVMのラッパーツールである。設定ファイル(Vargrantfile)を使用してVMを起動することができ、環境構築の再現性を高めてくれる。また、Dockerが64bitOSにしか対応していないのに対して、32bitOSをサポートしているため、環境構築のホストOSへの依存度も低下する。

ここでは、vagrantのインストール及びvagrantを使ってdockerコンテナを扱う際の基礎的なコマンドについて記載する。

vagrantはパッケージでインストールする。

vagrantはデフォルトの仮想化ソフトに VirtualBox または VMWare を使うため、どちらかインストールしておく。ここでは、VirtualBoxを利用する。

また、vagrantのプラグイン dotenv をインストールする。
$ vagrant plugin install dotenv
Installing the 'dotenv' plugin. This can take a few minutes...
Installed the plugin 'dotenv (2.0.1)'!

vagrantでVMを起動する
vargrant up

vagrantで起動したssh接続する
vargrant ssh

vagrantのVMのステータスを確認する
vagrant status

vagrantでVMを停止する
vagrant halt

ホストとのファイル共有
synced_folderを使用してローカルマシンとVMの間で特定のディレクトリを共有する。
ソースコードやDockerfileを変更して、そのままVM上でDockerイメージをビルドするために用いる。

      config.vm.synced_folder(
        "../",
        "/home/core/#{File.basename(File.dirname(Dir.pwd))}",
        id: "vmid",
        :nfs => true,
        :mount_options => ['nolock,vers=3,udp'],
      )

Dockerfileを書く際の基本方針
イメージを小さくすること。コンテナの起動、イメージのビルド、push、pullにかかる時間を短くすることができる。

ビルドのレイヤー数
Dockerfileには、関連するコマンドを連結(&&とバックスラッシュを利用すると、コマンドを連結できる)してビルドのステップ数を少なくする。

Vagrantfileとansibleでdockerをインストールする方法はこちら


2015年6月8日月曜日

dockerを使用したシステム設計の原則

dockerを用いてシステム設計する際に注意すべきことがある。1バーチャルマシン(つまりサーバ1台)として捉えるとうまく設計できない。

dockerを用いる場合、「再現性」、「構築フロー」、「デベロッパーがサーバ管理できる」といったメリットがある。

しかし、コンテナやホストマシンの管理、docker自体の頻繁なアップデートによる学習コストなどがデメリットである。

システム設計で大切なこと
dockerコンテナの運用方法自体が簡潔になる方向を模索する。

1コンテナは1デーモンと考える
コンテナはサーバ上で起動する1デーモンと考えること。

コンテナに永続化するデータが存在しないこと
ログを含む永続化対象のデータはすべて、コンテナ外へ保存すること。

設定はすべてアプリケーションコード外で管理すること
設定がアプリケーションコードに依存していると、アプリケーションの変更によって接続先が変わってします可能性があるため。

ホストマシンには出来る限り直接パッケージをインストールしないこと
ミドルウェアを含めた環境全体をコンテナとして動かすこと。
ホストマシンの管理を不要にし、可能な限りコンテナのアップデートコストのみにする。

buildには基本Dockerfileを使用すること
シンプルで簡単だからである。

dockerを扱う際のコマンド

dockerで使用する基礎的なコマンド。環境用のツールにはboot2dockerを使用する。

仮想マシンの初期化
boot2docker init

1回だけ実行すれば良い。2回目は以下の様なメッセージが表示される。
Virtual machine boot2docker-vm already exists

仮想マシンの起動
boot2docker start

シェルの初期化
$(boot2docker shellinit)

dockerのバージョン確認
docker -v

dockerイメージ一覧の表示
docker imges

dockerイメージの起動
docker run -p <local ポート番号>:<コンテナ無いのポート番号> <Dockerイメージ名> <コンテナで実行するコマンド> <引数>

ローカルにイメージが無い場合はpullしてからrunが実行される
コンテナは使い捨てである。ファイルに変更が無い場合、buildしてから使用する。


dockerイメージの作成(コンテナからイメージを作成する)
buildコマンドを使う。buildコマンドは「Dockerfile」ファイルと適用してDockerイメージを作成するコマンドである。(commitはミスの原因になるので使用しない)
docker build -t=<イメージ名:{タグ名}> <Dockerfileを含むコンテナのディレクトリ>

dockerイメージをDocker Hubなどのレジストリにpushする
docker push <イメージ名>


Docker Hubへのログイン
docker login
ユーザ名、パスワード、メールアドレスを入力する

Docker Hub は Dockerレジストリの1つである。Docker社が公式で提供するレジストリであり、イメージの共有をすることができる。


Dockerfileについて
Dockerfileはmakeに対するMakefileのようなもので、Dockerコンテナの構成ファイルを記述するテキストファイルである。以下が参考になる。

2015年6月6日土曜日

Python Vim 開発設定

Vimを使ってPython開発を快適に行うための設定

開発する上で目指したいのは、以下の様なことではないだろうか。

  • コードの記述量を減らす
  • コードの書式チェックを自動で行う
  • テキストエディタ同様の表示書式
  • 記述中にインタプリタを実行する

設定方法

コードの記述量を減らす

Python補完のVimプラグインを使用するため、jedi-vimを導入する。
以下のページがシンプルでわかりやすい。jedi-vimのインストール部分だけ実行すれば良い。
Vim+Python環境設定メモ


コードの書式チェックを自動で行う
コードの保存時などに、コードをチェックする。

sudo pip install flake8 pyflakes pep8 pylint


テキストエディタ同様の表示書式
~/.vimrcに以下を追加する。

"#####表示設定#####
set number "行番号を表示する
set title "編集中のファイル名を表示
set showmatch "括弧入力時の対応する括弧を表示
syntax on "コードの色分け
set tabstop=4 "インデントをスペース4つ分に設定
set smartindent "オートインデント

"#####検索設定#####
set ignorecase "大文字/小文字の区別なく検索する
set smartcase "検索文字列に大文字が含まれている場合は区別して検索する
set wrapscan "検索時に最後まで行ったら最初に戻る


記述中にインタプリタを実行する
~/.vimrcに以下を追加する。rubyとperlの実行も追加しておくと便利。

autocmd BufNewFile,BufRead *.rb nnoremap <C-e> :!ruby %
autocmd BufNewFile,BufRead *.py nnoremap <C-e> :!python %
autocmd BufNewFile,BufRead *.pl nnoremap <C-e> :!perl %

これでだいぶ作業しやすくなるはず。

2015年6月2日火曜日

Convolutional Neural Network の 畳込み層を出力してみた

オリジナルのCNN実装を使って、Le-Net5でMNISTデータを学習した後の畳み込み層を出力してみた。

conv1





conv2



conv3


2015年6月1日月曜日

lombok (Java)

Lombokはボイラープレートコード(getter/setterなどの決まりきっているが記述が必要なコード)を簡潔に記述するためのライブラリである。

@Data
getter/setterをコンパイル時に記述するアノテーション

package jp.masazdream.ds;
import lombok.Data;
/**
* Lombokのサンプルクラス
*
* @author masai
*
*/
@Data
public class Lombok {
private String mA;
private String mB;
Lombok(String a, String b){
mA = a;
mB = b;
}
}
view raw Lombok.java hosted with ❤ by GitHub
ただし、staticなメンバ変数については@Dataや@Getterを指定してもgetterが宣言されないので注意すること。

@Builder
を利用すると、コンストラクタとgetterが宣言される。

Gradleでビルドする場合は、依存関係に以下を追加する。

dependencies {
...
provided 'org.projectlombok:lombok:1.12.2'
}