2014年1月31日金曜日

Java 8を使ってみる(使用編・Stream編1)

Java 8には、Streamというクラスが追加されています。
面倒くさい説明を少々しますので飛ばす人はこちらへ→スキップ


StreamはC#でいうIEnumbleに準ずるもので、中間処理を実行しても実際の絞り込みは
実行されず、終端処理を呼び出された際に実行されるというものです。

下の例のような記述ができます。

int sumOfRedWeights  = blocks.stream().filter(b -> b.getColor() == RED)
                                           .mapToInt(b -> b.getWeight())
                                           .sum();

ここでは、blocksをgetColor()==REDで絞り込み(filter)、
getWeight()を抽出して(mapToInt)合計しています(sum)。
中間処理のfilter、mapToIntでは絞込は行われず、
終端処理のsumが呼ばれる際に遅延的に実行されます。


言い換えると、繰り返し処理を関数的に記述できるようになるというところでしょうか。
また、終端処理まで状態を保持しないため並列化にも適しています。
stream()のかわりにparallelStream()を使うことによって、処理を並列的に実行します。
サーバではもともとマルチスレッドなので他のコアも処理してたりして、
あまり恩恵ないかもしれないですが、可読性と並列化が捗りますね\(^o^)/。



ということで、
StreamとSQLのクエリと比較してみましょう。


単純なSELECTでTestMember.idのみを抽出する

//SQL
SELECT TestMember.id

//Listに変換
List list = members.stream().map(TestMember::getId)
        .collect(Collectors.toList());


//Setに変換
Set set = members.stream().map(TestMember::getId)
        .collect(Collectors.toCollection(TreeSet::new));


TestMember::getIdは関数渡しでメソッドを渡しています。
map中でgetIdを呼び出した値を利用しています。

 

Member.weightの合計を求める

//SQL
SELECT sum(TestMember.weight)



total = members.stream()
        .mapToInt(x -> x.getWeight())
        .sum();

合計を求める方法には、Collectorを使う方法もあります

int total = members.stream()
        .collect(Collectors.summingInt(x -> x.getWeight()));


平均を求める

//SQL
SELECT avg(TestMember.weight)



double average = members.stream()
        .mapToInt(x -> x.getWeight())
        .average()   
        .orElse(0d);

orElse()というメソッドが出てきました。
average()は、OptionalDoubleというNull許容なオブジェクトを返します。
OptionalDoubleのNullの場合のデフォルト値を指定してdoubleを返すのがorElse()です。
OptionalDoubleは、C#でいうところのdouble?ですね。


切り出し

何番目の要素を指定して取り出すということもできます。


//SQL
SELECT TestMember.weight LIMIT 1

members.stream()
.mapToInt(x -> x.getWeight())
.findFirst()
.orElse(0);


//SQL
SELECT * OFFSET 1 LIMIT 2
members.stream()
.skip(1)
.limit(2).toArray(); 



グループ・パーティション

Stream には group byや partitioned byに当たるメソッドもあります

//グループ分け
Map         = members.stream()
                    .collect(Collectors.groupingBy(x -> x.getPart()));


同じpartのTestMemberをpart毎のマップにする。


本当は、groupingByには関数渡しで、TestMember::getPartとできるはずが、
エラーになっちゃいますね。


//パーティション
Map        members.stream()
                .collect(Collectors.partitioningBy(s -> s.getId() >= 5));




その他

条件に一致したものが一件でもある

boolean match = members.stream().anyMatch(b -> b.getId() > 5);

countを使うより軽いはず?


weightでソートする
ソートをstream、ラムダ式で実装してみた

members.stream().sorted((r,l) -> {
       return Integer.compare(r.weight, l.weight);
}).collect(Collectors.toList());   

ソートするだけならもっと効率のいい方法があるかな
parallelStreamが使えるから早いのか?むむ?



今回使ったプロジェクト->StreamSample.zip

次回もStream編がつづく

2014年1月28日火曜日

OpenCV for iOS 1/27, 2014 ビルド

1/27, 2014現在のgitリポジトリからダウンロードした
OpenCV はiOS向けにはそのままだとビルドできません。
そのための修正をメモ 


ソース修正

https://stackoverflow.com/questions/16983696/how-to-build-opencv-2-4-9-for-ios/17025423#17025423

対象ファイルの指定行を追加修正

3rdParty/libjpeg/CMakeLists.txt:12, jmemansi.c をビルドから除く(これはすでに適応済み?)

if(ANDROID OR IOS)
  ocv_list_filterout(lib_srcs jmemansi.c)
else()
  ocv_list_filterout(lib_srcs jmemnobs.c)
endif() 
 
modules/world/CMakeLists.txt:105 追加
ocv_list_filterout(objlist jmemansi) # <<= dirty fix
 
 

arm64対応

https://stackoverflow.com/questions/18976893/how-to-compile-opencv-for-ios7-arm64 

arm64ビルドは諦めて、armv7, armv7sのみのビルドに変更する

opencv/3rdparty/zlib/ にある gzlib.c, gzread.c, gzwrite.c  のincludeに追加

#include <unistd.h>
 
opencv/platforms/ios/build_framework.py  行99、100: 変更

targets = ["iPhoneOS", "iPhoneOS", ""iPhoneSimulator", "iPhoneSimulator"] 
archs = ["armv7", "armv7s", "i386", "x86_64"]

arm64を削除します
なくても、iPhone5sでも動く(プロジェクトがarmv7,armv7sでビルドされる場合)ので取りあえずなしでビルド




ビルドする


ビルド環境のパスを通す
sudo ln -s /Applications/Xcode.app/Contents/Developer /Developer
 
cmakeない場合は、portからinstall
sudo port install cmake

 
ビルド
python platforms/ios/build_framework.py ios



※モバイル系の記事は人気あるなあ

2014年1月23日木曜日

Java 8を使ってみる (Timeクラス編)

Java 8を使ってみよう 3回目は、Timeクラスです。


前回はインターフェースを扱いました。流れ的に次はラムダ式かStreamかと思ったのですが、
まとめるのが難しいので先に箸休めでTimeクラスを紹介します。


さて、Dateクラス使うの面倒臭いと思いませんか。
日付処理をしようとするとCalendarクラスを使わないといけないし、
差分とかとっていろいろしたいけど…、
という要望に答えた、CalenerクラスとDateクラス(+α)を統合した
のがJava 8からのTimeクラスです。



DateTime


時間を表すクラスには、いくつかあります。
代表的なのは、
LocalDateTime : タイムゾーンを含まない、日時
ZonedDateTime : タイムゾーンを含む、日時
になります。
ほかにもいろいろ追加されてますが、ここではLocalDateTimeを扱います。


日付処理


今日の3時のDateクラスを作成したいとなった時に今までは、
Calendarクラスを使っていました。

Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 3);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);

Date date = cal.getTime();


LocalDateTimeでは、クラスに日付処理が組み込まれているので、
Calendarのようなクラスを呼ぶ必要はありません。

 LocalDateTime datetime = LocalDateTime.now()
                .withHour(3).withMinute(0).withSecond(0).withNano(0);

指定日時のDateも作成しやすくなりました。

LocalDateTime datetime1 = LocalDateTime.of(2014, 5, 21, 1, 12);


一行で書けます。Javaではちょっと前から、一行で書くというのに
狂信的な人たちがいますね。





日付差分


Dateクラスは比較がafter,beforeくらいだったのですが
Java 8からは、専用のクラスが追加されてかなり拡張されています。
むしろこちらの方が嬉しい

ふたつのLocalDateTimeクラスの差分をDurationクラスで表せます。

Duration duration = Duration.between(datetime, datetime1);

このdurationは、datetimeからdatetime1までのナノ秒を保持しています。
LocalDateTimeクラスに、plusすることでdurationの秒数を追加できます。

LocalDateTime datePlused = datetime.plus(duration);

他にもminusとかできます。


Durationがあると、C#のTimeSpanのようなことができます。
この記事の内容をTimeSpanからDurationに置き換えると次のようになる。

public static final TimeSpan TIMESPAN = TimeSpan.Days(7);



Duration duration1 = Duration.ofDays(1);


で現在と差分を足したDateを求めるには、

Date date = TIMESPAN.getDate(new Date());
 


now.plus(duration1);

とすればいい。


何に利用するかというと主に
Memcachedに渡すExpireの 定義とかです。


Memcached.get(key, new Memgetable<String>() {
    public String get() {
        return value;
    }
}, TIMESPAN.getDate(new Date()));



Memcached.get(key, () -> {
        return value;
 }, Date.from(now.plus(duration1).toInstant());

こんな風に記述ができます。


まだ、*DateTimeに対応してないのでDateに直さないといけないのが面倒


つづく




2014年1月20日月曜日

Java 8を使ってみる(使用編・インターフェース)

準備編のつづきです。
今回はJava 8 を実際に使ってみましょう。


重みづけ抽選のロジック(以下RandomUtil)を、Java 8だとどうなるのか見ていきます。
ここでは、主にインターフェースの変更点についてみていきます。


FunctionalInterface


Java 8はインターフェースにFunctionalInterfaceというものが追加されました。
インターフェースに対して@FunctionalInterface宣言をするまたは、
一つのメソッドのみ定義するとFunctionalInterfaceと認識されます

RandomUtilだと、

public interface Weight<T> {
    int getWeight(T temp) ;
}

はJava 8ではFunctionalInterfaceとなります。

@FunctionalInterface
public interface Weight<T> {
    int getWeight(T temp) ;
}

と宣言をつけたものと同じ意味です。


FunctionalInterfaceにはラムダ式を渡すことができます。


TestMember member = RandomUtil.weightedRandom(members,
                    new Weight<TestMember>() {
                        public int getWeight(TestMember m) {
                            return m.weight;
                        }
                    });

以前までは、上記のように関数を渡していたのですが、
ラムダ式を渡すようにすると、次のように簡略化できます。

TestMember member = RandomUtil.weightedRandom(members,
                    m -> {
                        return m.weight;
                    });



Default Method


また、インターフェースにDefaultMethodを追加できるようになりました。


public interface Weight<T> {
    default int getWeight(T temp) {
        return 1;
    }
}

上のように定義した場合、

TestMember member = RandomUtil.weightedRandom(members,
                    new Weight<TestMember>() { });

とすると、getWeight の定義をしなくてもDefaultMethodとして定義している
メソッドが呼ばれます。ちなみに、DefaultMethodを定義していると
FunctionalInterfaceにはできません。



つづく


使用したプロジェクト:RandomUtil1.zip








2014年1月19日日曜日

Java 8を使ってみる(準備編)

遅ればせながら、Java8を触ってみようと思います。
以前、昨年の5月かそれぐらいに、一度動かしてみようと思っていたのですが
対応するIDEもなく、あきらめて日数がかなりたってしまいました。


Java 8の特徴は、なんといってもラムダ式の導入と
C#でIEnumbleにあたるStreamを導入し、Linqのような実装ができるようになるということです。
C#もつかう身としてJavaでLinqポイ実装ができるのは感慨もひとしおです。



まずは、Java 8が動作する環境を作ります。
Java 8はまだ正式リリースではないので、あくまでも試験的なものです。


Java 8 JDKのインストール


JDK 8 Early Access Releasesをダウンロードします。

https://jdk8.java.net/download.html

から環境に適したものをダウンロード、インストールします。


Eclipseのインストール


Eclipseというか、Spring Tool SuiteにJava8 supportのプラグインが
すでに入ったものがあるのでそれを利用します。


http://dist.springsource.com/snapshot/STS/nightly-distributions.html

上記のURLから
Spring Tool Suite - based on Eclipse 4.3 - including Java8 support
とあるところから、環境に適したものをダウンロードして、適当な場所に解凍します。



Eclipseの設定


解凍したフォルダにあるSTS.exeを起動します。
起動したらまず、JDkのパスを通しましょう

Window[メニュー] > Preferences > Java > InstalledJREs

からインストールしたJDK 8 ("C:\Program Files\Java\jdk1.8.0")を追加します。



プロジェクトの追加


Java 8に対応したプロジェクトを追加してみましょう
プロジェクト追加する際に、
Use an execution environment JREからJavaSE-1.8を選びます。
これで、プロジェクトでJava 8が利用できます。


準備編はここまで、使用編につづきます


2014年1月18日土曜日

Windows8 でのmecab javaバインディング ビルド

mecabとjavaバインディングをビルドしたのでそのメモ
環境は、Windows 8.1 x64, Visual C++ 2010 Express
           mecab0.996

インストール


Windows Software Development Kit (SDK) for Windows 8.1 をインストール
http://msdn.microsoft.com/en-US/windows/desktop/bg162891


mecabソースをダウンロード
https://mecab.googlecode.com/files/mecab-java-0.996.tar.gz
https://mecab.googlecode.com/files/mecab-0.996.tar.gz


 

libmecabビルド

 

 Configure

Configureする必要がるのか?
取り敢えず、MsysでConfigureする

cd mecab-0.996
./configure

make.bat修正


mecab-0.996/src/make.bat のパスを環境に合わせて修正
私の環境では次のように、上三行を変更

Set PATH=C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin;%PATH%
Set INCLUDE=C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include;%INCLUDE%
Set LIB=C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Lib;%LIB%


libmecab.dllを作成

mecab-0.996/src/make.batを実行

mecab-0.996にlibmecab.dllができる


 

 

Javaバインディング



用意されてるバインディング(MeCab_wrap.cxx)だと動かないので
自分で作成する

mecab-0.996/src/mecab.h を修正する

「#ifndef SIWG」を「#ifndef SWIG」に全部修正(何か所かある)

swigを実行する

cd mecab-0.996\swig
swig -java -package org.chasen.mecab -c++ MeCab.i

MeCab_wrap.cxxが生成される
swigがない場合はここから、パスを通しても完全パス指定でもOK


MeCab_wrap.dllを作成


mecab-java-0.997に作成したlibmecab.dllとMeCab_wrap.cxxをコピー
下のようなコマンドを実行する。パスは環境に合わせて変更

Set PATH=C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin;%PATH%
Set INCLUDE=C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include;%INCLUDE%
Set LIB=C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Lib;%LIB%


cl /EHsc /LD /I mecab-0.996\src /I "C:\Program Files (x86)\Java\jdk1.7.0_09\include" /I "C:\Program Files (x86)\Java\jdk1.7.0_09\include\win32" MeCab_wrap.cxx libmecab.dll


 

動作確認

libmecab.dllとMeCab_wrap.dllをコピーして 
適当なサンプルを実行


参考サイト:
http://ftablog.s56.xrea.com/index.php?itemid=167
内容はほぼ同じ(Windows7,mecab0.97)

2014年1月17日金曜日

Android NDKビルド環境をUbuntu Serverに作ってみた

AndroidのNDKビルド用の環境はWindowsやMac上でも作れるのですが、
Windowsだとcygwinとか面倒くさいし
Macだとたまにldtoolがないとか怒られるので
VMWare上のUbuntuに作成することにしました。
これだとコピペで環境を保存したり増やしたりえきるので便利ですね。


VMWare上にすでにUbuntuがインストールされていることが前提です。
Serverなのは消費メモリが少ないとかリソースの問題です。

Build環境
sudo apt-get install build-essential

1.Javaのインストール

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get install oracle-java7-installer 
 
 

2. Android NDKインストール

 
Android NDKをダウンロードします 

tar -xvjof android-ndk-r9c-linux-x86_64.tar.bz2
sudo mv ./android-ndk-r9c /usr/local/
echo 'export PATH=$PATH:/usr/local/android-ndk-r9c' >> .bashrc 
echo 'export NDKROOT=/usr/local/android-ndk-r9c' >> .bashrc 
 
 


3. Android SDK インストール

 
Android SDKをダウンロードします
unzip adt-bundle-linux-x86_64-20131030.zip
sudo mv adt-bundle-linux-x86_64-20131030 /usr/local/

echo 'export PATH=$PATH:/usr/local/adt-bundle-linux-x86_64-20131030/sdk/tools' >> .bashrc 
echo 'export PATH=$PATH:/usr/local/adt-bundle-linux-x86_64-20131030/sdk/platform-tools' >> .bashrc 
 


ライセンス許可

Android SDKの実行には最初にライセンスの認証が必要です
 
cd /usr/local/adt-bundle-linux-x86_64-20131030/sdk/tools
echo "y" | ./android update sdk --no-ui


 

SDK一覧

次のコマンドでSDKの一覧が表示されます
./android list sdk

 

SDKアップデート

必要なSDKをインストール、アップデートします
android update sdk -u --filter platform-tools,android-16,android-8,extra-android-support

 
 
動かしてみる
ndk-buildを実行してみる 
 
 

4.ビルドする際のメモ

 
個人的にAndroid.mkを利用するよりも
Configure時にSysroot, CC, CFLAG, LD, AR等を指定して
クロスコンパイルするほうがiOSと同じ要領なので楽

lib作成時にだいたいは次のようにして、static libraryを作って
あとで、static libraryを参照するAndroid.mkでsoにします
 
build.sh :  
 
BINPATH=$NDK_ROOT/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin
export CROSS_COMPILE=$BINPATH/arm-linux-androideabi
export CC=${CROSS_COMPILE}-gcc
export CXX=${CROSS_COMPILE}-g++
export LD=${CROSS_COMPILE}-ld
export AR=${CROSS_COMPILE}-ar
export CFLAGS="--sysroot $SYSROOT -std=gnu99 -L$LIB_DIR -L${NDK_ROOT}/usr/lib -I${NDK_ROOT}/usr/include"
 
./configure --prefix="一時フォルダ" --enable-static --disable-shared --host=arm-linux-androideabi

 
 
※おそらくConfigure時にarm-linux-androideabiの定義がないと怒られるので
下のリンクからconfigure.guess, configure.subをダウンロードして
configureのあるフォルダに移動、上書きします 
http://git.savannah.gnu.org/gitweb/?p=automake.git;a=blob_plain;f=lib/config.guess
http://git.savannah.gnu.org/gitweb/?p=automake.git;a=blob_plain;f=lib/config.sub


 
Android.mk : 
この辺りが参考になるかと 
https://groups.google.com/forum/#!topic/android-group-japan/NMoJAioGi04



2014年1月15日水曜日

Windows 実行時、引数の最大長

Windowsでexeを実行する際の引数の最大長について
調べたのでメモ

 Windowsのバージョンによっても違いますが
ここではWindows7以降の話



実行ファイルに渡すことのできる引数の最大長 32767 文字
これはUnicodeの制限から来ています

CMD.EXEからの実行の場合、
実行ファイル名も含めて、 8192文字
CMD.EXEの制限です

C#.net等からProcessを作成する場合、2048文字(たぶん)
UseShellExecute = falseにすればいいという噂もありますが、確認できませんでした



CMD.EXEから8192文字 以上の引数を実行 できないのは
結構ネックです。batファイルが実行できないですから。
CMD.EXEではなく、Shellを変更して,PowerShellやMsysを利用すれば
32767 文字まで利用可能なようです。
なので、batで動かない時はpsに拡張子を変更しましょう。


※長い引数を使うよりは、なるべくファイルで渡すべきですが