木曜日, 12月 31, 2015

Scala - Sequenceマッチ

Scalaのmatch文は極めて強力で,そのうちの一つのリスト要素へのパターンマッチも便利なものの一つだろう.以下の例はMyCatへのコンストラクタマッチとリスト要素へのシーケンスパターンマッチの組み合わせの例.もしパターンマッチ内でジェネリック型も含めた型パターンマッチ等をしていて,かつJavaからそのscalaコードから作られたクラスを利用する場合は,Javaの型消去の特性に気を付けるように.


object Matcher {

  def main(args:Array[String]):Unit = {
    val nekoz = genCats()
    nekoz.foreach{ x =>
      x match {
        case MyCat(a,b) if a > 5 => println("Adult neko.")
        case _ => println("..")
      }
    }
    nekoz match {
      case l @ List(MyCat(8, _), _*) => println(s"okay, starting with 8 year old cat - $l")
      case _ => println("..")
   }

  }


  def genCats() = {
    MyCat(8, "Tora") :: MyCat(4, "Mikeyo") :: List()
  }

}

case class MyCat(age:Int, name:String)


水曜日, 12月 30, 2015

Scala - ワードカウント

SparkではreduceByKeyで簡単にワードカウントができるが,scalaではfoldLeftで実装出来る.

    val x = """kiji,saru,saru#saru,inu,kiji,saru#inu,kiji,kiji#kiji#inu,inu#saru"""
    val v = x.split("#").flatMap(_.split(",")).map(s => (s,1))
    val z = v.foldLeft(Map[String,Int]())((accm, elem) => {
      if(accm.contains(elem._1))
        accm.updated(elem._1, elem._2 + accm(elem._1))
      else
        accm + elem
    })

火曜日, 12月 29, 2015

scala - ディレクトリのファイル操作

妻のパソコンの,とあるフォルダにある画像ファイルのタイムスタンプが何故かおかしくなってしまった.ファイルの数が膨大ということもあり,scalaでさっとコードを書いて修正した.

import java.io.File
import java.time.{LocalDateTime, ZoneId}
import java.util.Date

import org.apache.commons.io.FileUtils

/**
  * Created by neko32 on 2015/12/28.
  */
class FileDateChanger(preProcessDir:String, postProcessDir:String) {

  implicit def dateToLDate(d:Date):LocalDateTime = {
    d.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
  }

  def process(): Unit = {
    clean()
    val files = new File(preProcessDir).listFiles().filter(f => dateToLDate(new Date(f.lastModified())).getYear() == 2007)
    val totalNum = files.size
    var at = 0
    println(s"Total number of files - ${totalNum}")
    files.foreach { f =>
      var dat = new Date(f.lastModified())
      var f2 = new File(postProcessDir + "\\" + f.getName())
      println(s"processing .. ${f.getName()}[${new Date(f.lastModified())}]")
      FileUtils.copyFile(f, f2)
      f2.setLastModified(dat.plusYears(8).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli)
      println(s"to ${new Date(f2.lastModified())}")
      if(at % 10 == 0) println(s"processed ${at}")
      at += 1
    }
    println(s"done. # of processed files [${at}]")
  }

  private def clean(): Unit = {
    println(s"cleaning ${postProcessDir}")
    new File(postProcessDir).listFiles().foreach(_.delete())
    println("cleaning done.")
  }
}

object Runner {
  def main(args:Array[String]) = {
    val pre = "C:\\tmp\\pre_process"
    val post = "C:\\tmp\\post_process"
    new FileDateChanger(pre, post).process()
  }
}

日曜日, 12月 27, 2015

Scala - 乱数生成プロバイダを用いるstream


    val r = new Random(System.currentTimeMillis())
    def rand(max:Int):Stream[Int] = Stream.cons(r.nextInt(max), rand(max))
    rand(100) take 10 foreach println
    // assign index starting from 1. zipWithIndex()'s index starts from 0
    rand(50) take 5 zip(Stream from 1) foreach println

金曜日, 12月 25, 2015

Scala - Enumeration

ScalaでEnumerationを継承してenumを定義する例.

object Weekdays extends Enumeration {
  type WEEKDAY = Value
  val MON, TUE, WED = Value
}

object MyMain {

  def isMonday(w:Weekdays.Value) = {
    w == Weekdays.MON
  }

  def main(args:Array[String]) = {
    val w = Weekdays.MON
    val w2 = Weekdays.WED
    println(isMonday(w))
    println(isMonday(w2))
  }
}

木曜日, 12月 24, 2015

Scala - akka アクタ

Akka actorのサンプルメモ..

import akka.actor._

/**
  * Created by neko32 on 2015/12/23.
  */

sealed abstract trait Message
case class Request(a:Int, b:Int, op:Operator) extends Message
case class Response(a:Int, b:Int, op:Operator, result:Int) extends Message
case class ExecStart(cmd:String) extends Message
case class ExecEnd(a:Any) extends Message

sealed abstract class Operator(a:Int, b:Int) {
  def calc():Int
}
case class Plus(a:Int, b:Int) extends Operator(a,b) {
  override def calc() = a + b
  override def toString() = " + "
}
case class Minus(a:Int, b:Int) extends Operator(a,b) {
  override def calc() = a - b
  override def toString() = " - "
}
case class Multiply(a:Int, b:Int) extends Operator(a,b) {
  override def calc() = a * b
  override def toString() = " * "
}
case class Divide(a:Int, b:Int) extends Operator(a,b) {
  require(b != 0, "zero divide not allowed")
  override def calc() = a / b
  override def toString() = " / "
}

class CalcWorker extends Actor {
  override def receive = {
    case Request(a,b,op) if op.isInstanceOf[Plus] => println("sending.."); sender ! Response(a, b, op, a + b)
    case Request(a,b,op) if op.isInstanceOf[Minus] => sender ! Response(a, b, op, a - b)
    case Request(a,b,op) if op.isInstanceOf[Multiply] => sender ! Response(a, b, op, a * b)
    case Request(a,b,op) if op.isInstanceOf[Divide] => sender ! Response(a, b, op, a / b)
    case ExecEnd => context.stop(self)
  }
}

trait Requester extends Actor with ActorLogging {
  override def receive = {
    case Response(a, b, op, rez) =>
      log.info(s"got result from calc agent .. ${a}${op}${b} = ${rez}")
    case ExecStart(cmd) => run(cmd)
    case ExecEnd => context.stop(self)
    case _ => log.info("....")
  }
  def run(cmd:String)
}

class CalcRequester(calcAgent:ActorRef) extends Requester {

  def toOp(vals:Array[String]):Operator = vals(2) match {
    case s if s == "plus" => Plus(vals(0).toInt, vals(1).toInt)
    case s if s == "minus" => Minus(vals(0).toInt, vals(1).toInt)
    case s if s == "mult" => Multiply(vals(0).toInt, vals(1).toInt)
    case s if s == "div" => Divide(vals(0).toInt, vals(1).toInt)
  }

  override def run(cmd:String) = {
      // parse a,b
      assume(cmd.count(_ == ',') == 2)
      val vals = cmd.toLowerCase().split(",")
      println("parsed input - " + vals(0) + "," + vals(1) + "," + vals(2))
      val res = calcAgent ! Request(vals(0).toInt, vals(1).toInt, toOp(vals))
  }
}

object MyApp {
  def main(args:Array[String]) = {
    val sys = ActorSystem("CalcSys")
    val calcAgent = sys.actorOf(Props[CalcWorker], "CalcAgent")
    val calcReq = sys.actorOf(Props(new CalcRequester(calcAgent)), "CalcReq")
    calcReq ! ExecStart("3,8,plus")
    Thread.sleep(3000)
    calcReq ! ExecEnd
    calcAgent ! ExecEnd
    val f = sys.terminate()
  }
}


水曜日, 12月 23, 2015

Scala - Mixin, traitそしてselfによる型間依存関係の表現

Scalaのmixinとtraitそしてselfアノテーションを利用することにより,Javaと比較してより明快に型間の依存関係を宣言出来る.Javaではミックスインが出来ない為,クラス間の依存関係はあるクラスのprivate or protectedフィールドとして表現されていることがご想像いただけるかと思う.

case class Neko(name:String,pattern:String, age:Int)

trait CatManage {
  self: CatManage with DefNekoCheck =>
  var nekoz:List[Neko]
  def init():Unit
  def lookupCat(name:String):Neko
}

trait DefNekoCheck {
  def ? (neko:Neko) = neko != null
}

trait NekoCheck extends DefNekoCheck {
  override def ? (neko:Neko) = neko.name == "NA"
}

trait NekozManage extends CatManage with NekoCheck {

  override var nekoz = List[Neko]()

  override def init():Unit = {
    nekoz ::= Neko("Mikeyo", "Mike", 3)
    nekoz ::= Neko("Tora", "Chatora", 8)
    nekoz ::= Neko("Powder", "Mike", 8)
    nekoz ::= Neko("Machiko", "Mike", 10)
  }

  override def lookupCat(name:String):Neko = {
    val n = nekoz find(_.name == name)
    n.getOrElse(Neko("NA", "NA", -1))
  }
}

trait Act {
  def start(f: => List[String])
}

trait App {
  self: Act with CatManage =>
  override def start(execPlan: => List[String]) = {
    init
    val l = execPlan
    l.foreach{ name =>
      lookupCat(name) match {
        case x if x.name == "NA" => println(s"${name} not found..")
        case x => println(x)
      }
    }
  }
}

日曜日, 12月 20, 2015

Scala - visitorパターンのようなもの

ScalaではVisitorパターンを割合簡潔に書くことが出来る.以下の例はあるデータベース上で接続・検索を実行するモジュールで,この例では通常のRDB, HiveそしてインメモリDBをサポートしているとする.また,(乱暴ではあるが簡略のために)RDBとHiveserver2はコネクトとクエリを同一の手順,インメモリはコネクトを必要としないと仮に想定している.


package tanuneko

/**
  * Created by neko32 on 2015/12/20.
  */
sealed abstract class DB(host:String, userId:String, passwd:String)

case class RegularRDB(host:String, userId:String, passwd:String) extends DB(host, userId, passwd)

case class InMemoryDB(userId:String, passwd:String) extends DB(null, userId, passwd)

case class HiveDB(host:String, userId:String, passwd:String) extends DB(host, userId, passwd)

case class ResultSet(data:String)

trait DBAction {
  def connect():Unit = {}
  def runQuery(str:String):ResultSet
}

class RegularDBAction(host:String, userId:String, passwd:String) extends DBAction {
  override def connect(): Unit = {
    println(s"connecting to ${host} with user[${userId}]")
  }
  override def runQuery(sql: String): ResultSet = {
    println(s"send query[${sql} to Database[${host}]")
    new ResultSet("returned data.")
  }
}

class InMemoryDBAction(userId:String, passwd:String) extends DBAction {
  override def runQuery(sql: String): ResultSet = {
    println(s"[inmem] running query[${sql}]")
    new ResultSet("returned data.")
  }
}

object Runner {
  def main(args:Array[String]):Unit = {
      val userId = "neko"
      val passwd = "miPasswd150X"
      val host = "myhost.org:16500"
      val sql = "select * from mydb.mytbl"

      def runQueryAfterConnect = (db:DB) => db match {
        case r:RegularRDB => {
          val act = new RegularDBAction(r.host, r.userId, r.passwd)
          act.connect()
          println(s"result - ${act.runQuery(sql)}")
        }
        case i:InMemoryDB => {
          val act = new InMemoryDBAction(i.userId, i.passwd)
          println(s"[inmem] result - ${act.runQuery(sql)}")
        }
        case r:HiveDB => {
          val act = new RegularDBAction(r.host, r.userId, r.passwd)
          act.connect()
          println(s"result - ${act.runQuery(sql)}")
        }
      }

      val sybase = new RegularRDB(host, userId, passwd)
      runQueryAfterConnect(sybase)
      val inmem = new InMemoryDB(userId, passwd)
      runQueryAfterConnect(inmem)
      val hive = new HiveDB(host, userId, passwd)
      runQueryAfterConnect(hive)
  }
}

月曜日, 12月 14, 2015

scala - tailrecアノテーション

scalaではtailrecアノテーションを使って末尾再帰の最適化をすることができる.
関数は再起呼び出しで終わっている必要がある.
  @tailrec
  def fact(i:BigInt, accm:BigInt):BigInt = {
    i match {
      case _ if i == 1 => accm
      case _ => fact(i - 1, i * accm)
    }
  }

火曜日, 12月 01, 2015

Javaの関数プログラミングで偏差

暇つぶしに統計の基礎の動画を見ていたら,サンプルのコードを書きたくなったので,メモとして..
この例ではカンマ区切りの名前と点数 の行からなるデータを読み込んで,平均,分散,偏差,標準偏差そして偏差値を計算する.

package org.tanuneko;

import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.common.io.LineProcessor;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.stream.Stream;

public class STDDev {

    public static void main(String args[]) throws Exception {
        new STDDev();
    }

    public STDDev() throws IOException {
        File scoreFile = new File(STDDev.class.getClassLoader().getResource("score.txt").getPath());
        ImmutableList<Record> rec = Files.asCharSource(scoreFile, Charset.defaultCharset())
                .readLines(new LineProcessor<ImmutableList<Record>>() {
                    List<Record> results = Lists.newArrayList();
                    @Override
                    public boolean processLine(String line) throws IOException {
                        List<String> elems = Splitter.on(",").splitToList(line);
                        Preconditions.checkState(elems.size() == 2);
                        results.add(new Record(elems.get(0), Integer.parseInt(elems.get(1))));
                        return true;
                    }

                    @Override
                    public ImmutableList<Record> getResult() {
                        return ImmutableList.copyOf(results);
                    }
                });

        int totalScore = rec.stream().mapToInt(x->x.getScore()).sum();
        double avg = totalScore / rec.size();
        rec.stream().forEach(x->x.setDev(x.getScore() - avg));
        double v = calcVariation(rec);
        double d = calcStddev(rec);

        System.out.println("total:" + totalScore + ", avg:" + avg);
        System.out.println("variation:" + v);
        System.out.println("stddev:" + d);
        System.out.println(checkDev(rec));

        rec.stream().forEach(x -> {
            x.setScoreDev(50 + 10 * (x.getDev()/d));
        });

        System.out.println(rec);
    }

    private double calcVariation(ImmutableList<Record> rec) {
        return rec.stream().mapToDouble(x->x.getDev())
                .reduce(0, (x,y)-> x + Math.pow(y, 2)) / rec.size();
    }

    private double calcStddev(ImmutableList<Record> rec) {
        return Math.sqrt(calcVariation(rec));
    }

    private boolean checkDev(ImmutableList<Record> rec) {
        double d = rec.stream().mapToDouble(x->x.getDev())
                .sum();
        System.out.println(d);
        return d == 0;
    }
}

class Record {
    private String name;
    private int score;
    private double dev;
    private double scoreDev;
    public Record(String name, int score) {
        this.name = name;
        this.score = score;
        dev = scoreDev = 0d;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                .add("name", name)
                .add("score", score)
                .add("dev", dev)
                .add("score dev", scoreDev)
                .toString();
    }

    public int getScore() {
        return score;
    }

    public double getDev() {
        return dev;
    }

    public void setDev(double dev) {
        this.dev = dev;
    }

    public double getScoreDev() {
        return scoreDev;
    }

    public void setScoreDev(double scoreDev) {
        this.scoreDev = scoreDev;
    }
}



火曜日, 11月 24, 2015

ねこ。。

google guavaの関数の合成やtransformを使ってみた.(今さらながら。。)
以下のコードはID:にゃんこクラスのマップのルックアップ関数とにゃんこクラスからそのにゃんこ情報のStringに変更する関数を合成したものと,
そのにゃんこ情報列からmで始まりかつみけねこである条件でフィルタをかける例.

package org.tanuneko;

import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;

import java.util.*;

public class Nekosagashi {

    public static void main(String args[]) {
        new Nekosagashi();
    }

    public Nekosagashi() {
        Map<Integer,Cat> neko = getMap();
        Function<Integer,Cat> lookup = Functions.forMap(neko);
        Function<Cat,String> patExt = new CatInfoExt();
        Function<Integer,String> getCatInfoAsStringByID = Functions.compose(patExt, lookup);
        List<Integer> vals = Arrays.asList(IVAL(1),IVAL(2),IVAL(3),IVAL(4));
        Collection<String> nekos = Collections2.transform(vals, getCatInfoAsStringByID);
        Collections2.filter(nekos,
                Predicates.and(new IsStartWithM(), new IsMikeneko()))
                .forEach(System.out::println);
    }

    class IsStartWithM implements Predicate<String> {
        @Override
        public boolean apply(String input) {
            return input.toLowerCase().startsWith("m");
        }
    }

    class IsMikeneko implements Predicate<String> {
        @Override
        public boolean apply(String input) {
            return input.toLowerCase().contains("mike");
        }
    }

    class Decorator implements Function<String,String> {

        @Override
        public String apply(String input) {
            return "***" + input + "***";
        }
    }

    class CatInfoExt implements Function<Cat,String> {

        @Override
        public String apply(Cat input) {
            return input.getName() + ":" + input.getPattern();
        }
    }

    public Map<Integer,Cat> getMap() {
        Map<Integer,Cat> map = new HashMap<>();
        map.put(1, new Cat("Powder", "seiyoumike"));
        map.put(2, new Cat("Machiko", "orangemike"));
        map.put(3, new Cat("Torachan", "chatora"));
        map.put(4, new Cat("Mikeyo", "wafumike"));
        return map;
    }

    class Cat {
        private String name;
        private String pattern;
        public Cat(String name, String pattern) {
            this.name = name;
            this.pattern = pattern;
        }
        public String getName() { return name; }
        public String getPattern() { return pattern; }
    }

    private static Integer IVAL(int i) {
        return new Integer(i);
    }
}

そしてこれはうちのめんこいにゃんこ.

日曜日, 11月 15, 2015

[scala] unapplyメモ

extractorとしてのunapplyのサンプルコードメモ.

Message.scala - メッセージ コンパニオンクラス
package tanuneko.ui2

class Message(val msg:String) {

  def this() = this(msg = "")

  override def toString():String = {
    s"msg:${msg}"
  }

  def show(): Unit = {
    println(msg)
  }

}

object Message {

  def apply(): Unit = new Message()
  def apply(msg:String) = new Message(msg)
  def unapply(res:Message) = Some(res.msg + "dayon")

}


MessageTestSpec.scala - メッセージspec2テスト

package tanuneko.test

import tanuneko.ui2.Message
import org.specs2._

class MessageTestSpec extends mutable.Specification {
  "Message unapply" should {
    "return its msg field" in {
      val resp = Message("nyanchan")
      var isOk = resp match {
        case Message(x) if x == "nyanchandayon" => true
        case _ => false
      }
      isOk must beTrue
    }
  }
}

木曜日, 9月 24, 2015

scala勉強メモ

些細なバブルソート.いいコード書けるように頑張ります.

object BubbleSort {
  def bubble(v:Array[Int]): Unit = {
    var t = 0
    def iter = (x:Array[Int]) => {
      for(i <- 0 until x.length)
        println(x(i))
    }

    println("**before**")
    iter(v)

    for(i <- 0 until v.size) {
      for(j <- i until v.size - 1) {
        if(v(i) >= v(j)) {
          t = v(i)
          v(i) = v(j)
          v(j) = t
        }
      }
    }
    println("**after**")
    iter(v)
  }

  def main(args:Array[String]): Unit = {
    var v = Array(32,25,40,10,38,50)
    bubble(v)
  }
}


日曜日, 8月 23, 2015

Scala始めます

最近仕事でどうやらSparkを使い始めることになりそう.
Sparkの調査過程で明らかになったのは,JavaでSparkのコードを書くべきでなく,scalaで書くべきだということ.(既にSparkを使われている方々にとっては当然のことだと思うが).

まず第一にコードの量が違いすぎる.JavaでのSparkコードは冗長である一方Scalaでのコードは短くエレガントだ.

問題は私自身のscala経験が無いので一から勉強である.
C++, bash, Javaそしてgroovyを主に使ってきた私にしてもscala習得はかなり歯ごたえがありそうな気がする.

以下のコードは一応私にとってhello worldのようなもの.年末までくらいに自身がどれくらいのscalaコードを書けるようになっているか楽しみである.


import scala.util.Random

object Test {
  def main(args: Array[String]) {
    val r = new Random()
    // does not work
    def l1 = r.shuffle((0 to 20).toList)
    def l2 = r.shuffle((0 until 20).toList)
    l1.foreach { it => println(it) }

    println(l1 sameElements l2)
  }
}

土曜日, 3月 28, 2015

リストのループ検出

リストのループ重複検出をするコードを書いてください,という質問は候補者が勉強をしてきたかどうかを見るのに加減の良い問題だと,昨今のインタビューをしていて思った次第である.

 様々な解の仕方があるが,やはりウサギさんとカメさんを期待するだろう.
#include <iostream>

class Node {
// to make code simpler, members are public
public:
 int _data;
 Node* _next;
 Node(int data) :_data(data) {
  _next = NULL;
 }
 ~Node(){}
 Node(Node& n) {
  _data = n._data;
  _next = n._next;
 }
 Node& operator=(Node& n) {
  _data = n._data;
  _next = n._next;
  return *this;
 }
};

void iterate(Node* root) {
 Node* p = root;
 int cnt = 0;
 while (p) {
  std::cout << p->_data << std::endl;
  p = p->_next;
  if (cnt++ >= 100) {
   std::cerr << "!!!! potential loop found" << std::endl;
   return;
  }
 }
}

void add(Node* root, int val) {
 Node* n = new Node(val);
 Node* p = root;
 while (p->_next) {
  p = p->_next;
 }
 p->_next = n;
}

bool detectLoop(Node* root) {
 Node* slow_ptr = root;
 Node* fast_ptr = root;
 while (slow_ptr && fast_ptr && fast_ptr->_next) {
  slow_ptr = slow_ptr->_next;
  fast_ptr = fast_ptr->_next->_next;
  std::cout << slow_ptr->_data << ":" << fast_ptr->_data << std::endl;
  if (slow_ptr == fast_ptr) {
   return true;
  }
 }
 return false;
}

int main() {
 Node r(10);
 add(&r, 32);
 add(&r, 64);
 add(&r, 128);
 // simulate loop here
 r._next->_next->_next->_next = r._next;
 iterate(&r);
 std::cout << "loop exists?" << (detectLoop(&r) ? "TRUE" : "FALSE") << std::endl;
 return 0;
}

日曜日, 1月 18, 2015

guice

現在のプロジェクトからguiceを使うことになったが,これはなかなかの優れものだと思う.
今まで使ったDIフレームワークと比べても,コードを簡潔に短くことが出来るようになったと思う.
  以下の例はFTPでファイルを送信するロジックを仮実装した例.コンフィグのバインドの例は決して真似をせぬ様.あくまでguiceの基本バインドの例である.


FileTransmit.java
ackage org.tanuneko;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;


public class FileTransmit {

    public static void main(String args[]) {

        String[] myArgs = {"/home/neko32", "tako.log", "640"};
        Config c = new AppConfig(myArgs);

        Injector injector = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                bind(FileSenderIF.class).to(FTPFileSender.class);
                bind(Config.class).toInstance(c);
            }
        });

        FileTransmitProgram ft = injector.getInstance(FileTransmitProgram.class);
        ft.run();
    }

    class MyAppConfig extends Config {
        MyAppConfig(String[] args) {
            // do not do this in product code
            super.logFileLocation = args[0];
            super.logFileName = args[1];
            super.maxFileSize = Integer.parseInt(args[2]);
        }
    }
}



FileTransmitProgram.java
package org.tanuneko;

import com.google.inject.Inject;

public class FileTransmitProgram {

    @Inject
    private FileSenderIF sender;
    @Inject
    private Config conf;


    public void run() {
        sender.setLogging(conf.getLogFileLocation(), conf.getLogFileName());
        sender.setFileMaxFileSize(conf.getMaxFileSize());
        sender.send("ABC", "DATA");
    }
}


FTPFileSender.java
package org.tanuneko;

public class FTPFileSender implements FileSenderIF {

    private int maxFileSize = -1;

    public void setLogging(String logLocation, String fileName) {
        System.out.println(String.format("Setting logs[%s/%s]", logLocation, fileName));
    }

    public void setFileMaxFileSize(int maxFileSize) {
        int prevMax = maxFileSize;
        this.maxFileSize = maxFileSize;
        System.out.println(String.format("max file size has changed from %d to %d", prevMax, this.maxFileSize));
    }

    @Override
    public void send(String fileName, String data) {
        System.out.println(String.format("file %s(%s) was sent via FTP", fileName, data));
    }
}

日曜日, 1月 11, 2015

Java8 - Function::compose

Java8のFunctionクラスのcompose()メソッドを使えば,簡単にメソッドチェインを実装できる.
上手く使えばコードをかなり簡潔にできるだろう.以下のコードは3つの文字列操作フィルタをチェインして適用させた例.

package org.tanuneko;

import java.util.function.Function;
import java.util.stream.Stream;

public class FuncComposer {

    public FuncComposer() {
    }

    public void run() throws Exception {
        Function<String,String> addPrefixFilter = x -> "***" + x;
        Function<String,String> addPostfixFilter = x -> x + "***";
        Function<String,String> toLowerCaseFilter = x -> x.toLowerCase();
        String base = "WOW";

        Function<String,String> composedFilter =
        Stream.of(addPrefixFilter, addPostfixFilter, toLowerCaseFilter)
                .reduce((func, nxt)->func.compose(nxt))
                .orElseGet(Function::identity);

        System.out.println(composedFilter.apply(base));
    }


    public static void main(String args[]) throws Exception {
        FuncComposer c = new FuncComposer();
        c.run();
    }
}

ElasticSearch IntegrationTest

ElasticSearch IntegrationTestを使えば,メモリ上での仮想クラスタ上で単体テストを走らせることができる優れものだが,あまりサンプルコードが見つからない気がする.


package org.tanuneko;

import org.apache.commons.io.FileUtils;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import org.junit.Test;

import java.io.File;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
import java.util.function.Function;

import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.core.Is.is;


@ClusterScope(scope= ElasticsearchIntegrationTest.Scope.SUITE,numDataNodes=2)
public class ESIntegrationTest extends ElasticsearchIntegrationTest {

    private static final String INDEX_NAME = "tanuapp";
    private static final String DOCTYPE_NAME = "posting_msg";
    private static final String TODAY_DATE_STR = new SimpleDateFormat("yyyy-MM-dd").format(new Date());

    @Test
    public void testSearch() throws Exception {
        createIndex(INDEX_NAME);
        client().admin().indices().preparePutMapping(INDEX_NAME)
                .setType(DOCTYPE_NAME)
                .setSource(readMappingFile(DOCTYPE_NAME + "_mappings.json"))
                .execute().actionGet();

        assertThat(indexDoc(INDEX_NAME, DOCTYPE_NAME, "1", "neko32", "hellou").isCreated(), is(true));
        assertThat(indexDoc(INDEX_NAME, DOCTYPE_NAME, "2", "ushi", "poooo").isCreated(), is(true));
        assertThat(indexDoc(INDEX_NAME, DOCTYPE_NAME, "3", "neko64", "ka-kakinkin ka-kinkin").isCreated(), is(true));

        flushAndRefresh(INDEX_NAME);
        ensureGreen(INDEX_NAME);
        assertThat(indexExists(INDEX_NAME), is(true));

        GetResponse resp = client().prepareGet(INDEX_NAME, DOCTYPE_NAME, "3")
                .execute()
                .actionGet();

        Map source = resp.getSource();
        assertThat((String)source.get("user"), is("neko64"));
        assertThat((String)source.get("message"), is("ka-kakinkin ka-kinkin"));
        final String postedDate = (String)source.get("postDate");
        Function isSameDate = x -> x.startsWith(TODAY_DATE_STR);
        assertTrue(isSameDate.apply(convertToGMT(postedDate)).booleanValue());

        SearchResponse searchResp = client().prepareSearch(INDEX_NAME)
                .setTypes(DOCTYPE_NAME)
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(QueryBuilders.fuzzyLikeThisFieldQuery("user").likeText("neko50"))
                .setPostFilter(FilterBuilders.idsFilter(DOCTYPE_NAME).ids("1"))
                .execute()
                .actionGet();

        assertThat(searchResp.getHits().getHits().length, is(1));
        assertThat(searchResp.getHits().getAt(0).getSource().get("user"), is("neko32"));
    }


    private String readMappingFile(final String fileName) throws Exception {
        return FileUtils.readFileToString(new File(ESIntegrationTest.class.getClassLoader().getResource(fileName).getPath()));
    }

    private String convertToGMT(String dateStr) {
        return LocalDateTime.parse(dateStr.substring(0,dateStr.length()-1)).toString();
    }

    private IndexResponse indexDoc(final String idxName,
                                    final String docName,
                                    final String id,
                                    final String user,
                                    final String msg) throws Exception {
        return client().prepareIndex(INDEX_NAME, DOCTYPE_NAME)
                .setSource(genMsg(user, msg))
                .setId(id)
                .execute().actionGet();
    }

    private XContentBuilder genMsg(String user, String msg) throws Exception {
        return jsonBuilder().startObject()
                .field("user", user)
                .field("postDate", new Date())
                .field("message", msg)
                .endObject();
    }
}