Hatena::Groupocaml-nagoya

yoshihiro503の関数的日記

2014-09-04 (Thu)Scalaで新しいパスワードをつくる

[Scala]Scalaで新しいパスワードをつくる

11:27 |  [Scala]Scalaで新しいパスワードをつくる - yoshihiro503の関数的日記 を含むブックマーク はてなブックマーク -  [Scala]Scalaで新しいパスワードをつくる - yoshihiro503の関数的日記

次の文字を含むランダムな文字列を生成する。

  • 大文字アルファベット
  • 小文字アルファベット
  • 数字
  • 記号: ` ~ ! @ # $ % ^ & * ( ) _ - + = { } [ ] \ | : ; " ' < > , . ? /
def makePassword(len : Int) = {
  import scala.util.Random
  def randomChoice[A](xs : Seq[A]) = xs(Random.nextInt(xs.length))
  val cs = ('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9') ++
   Seq('`', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '-', '+', '=', '{', '}', '[', ']', '\\', '|', ':', ';', '\"', '\'', '<', '>', ',', '.', '?', '/', ' ')
  Stream.continually(randomChoice(cs)).take(len).mkString
}

こういう時対話環境のある言語は便利。

2012-05-24 (Thu)

CentOSでocamlからmysqlを扱う

| 16:59 | CentOSでocamlからmysqlを扱う - yoshihiro503の関数的日記 を含むブックマーク はてなブックマーク - CentOSでocamlからmysqlを扱う - yoshihiro503の関数的日記

CentOS-6では yummysql-develをインストールしても、libmysqlclient.a が得られないようだ。

libmysqlclient.aがあるかどうかはlocateコマンドで確かめることができる。

 $ locate libmysqlclient
/usr/lib/mysql/libmysqlclient.so
/usr/lib/mysql/libmysqlclient.so.16
/usr/lib/mysql/libmysqlclient.so.16.0.0
/usr/lib/mysql/libmysqlclient_r.so
/usr/lib/mysql/libmysqlclient_r.so.16
/usr/lib/mysql/libmysqlclient_r.so.16.0.0
 $ locate libmysqlclient.a
 $

OCamlからMysqlを使う場合はocaml-mysql(執筆時version 1.1.0)を通常使うが、ocaml-mysqlのビルド時にlibmysqlclient.aがないとリンクがうまくされないという問題がある。これでは、ocaml-mysqlのインストール自体はエラーなく出来るが、それを使ったプログラムのコンパイルが失敗する。

例えば次のようなエラーメッセージが出てしまう。

/usr/local/lib/ocaml/site-lib/mysql/libmysql_stubs.a(mysql_stubs.o): In function `stmt_finalize':
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:805: undefined reference to `mysql_stmt_close'
/usr/local/lib/ocaml/site-lib/mysql/libmysql_stubs.a(mysql_stubs.o): In function `res_finalize':
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:350: undefined reference to `mysql_free_result'
/usr/local/lib/ocaml/site-lib/mysql/libmysql_stubs.a(mysql_stubs.o): In function `caml_mysql_stmt_status':
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:1080: undefined reference to `mysql_stmt_errno'
/usr/local/lib/ocaml/site-lib/mysql/libmysql_stubs.a(mysql_stubs.o): In function `caml_mysql_stmt_close':
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:859: undefined reference to `mysql_stmt_close'
/usr/local/lib/ocaml/site-lib/mysql/libmysql_stubs.a(mysql_stubs.o): In function `caml_mysql_stmt_insert_id':
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:1072: undefined reference to `mysql_stmt_insert_id'
/usr/local/lib/ocaml/site-lib/mysql/libmysql_stubs.a(mysql_stubs.o): In function `caml_mysql_stmt_affected':
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:1064: undefined reference to `mysql_stmt_affected_rows'
/usr/local/lib/ocaml/site-lib/mysql/libmysql_stubs.a(mysql_stubs.o): In function `caml_mysql_stmt_execute':
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:986: undefined reference to `mysql_stmt_param_count'
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:1000: undefined reference to `mysql_stmt_bind_param'
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:1008: undefined reference to `mysql_stmt_execute'
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:1018: undefined reference to `mysql_stmt_field_count'
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:1028: undefined reference to `mysql_stmt_bind_result'
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:1015: undefined reference to `mysql_stmt_error'
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:987: undefined reference to `mysql_stmt_param_count'
/usr/local/lib/ocaml/site-lib/mysql/libmysql_stubs.a(mysql_stubs.o): In function `db_fields':
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:605: undefined reference to `mysql_num_fields'
/usr/local/lib/ocaml/site-lib/mysql/libmysql_stubs.a(mysql_stubs.o): In function `db_proto_info':
/home/itp/archives/ocaml-mysql-1.1.0/mysql_stubs.c:636: undefined reference to `mysql_get_proto_info'
...

これを解決するためにはocaml-mysqlをビルドするときの configure で環境変数のLDFLAGSを設定しておけばよい。

例えば上で調べたように libmysqlclient.so の場所が /usr/lib/mysql/ だった場合、次のように ocaml-mysql をインストールし直せばよい。

 $ cd ocaml-mysql-1.1.0
 $ env LDFLAGS='-L/usr/lib/mysql' ./configure
 $ make clean
 $ make all
 $ make opt
 # make uninstall
 # make install

2012-05-23 (Wed)

omakeをocaml-3.12.1でビルドする

| 16:59 | omakeをocaml-3.12.1でビルドする - yoshihiro503の関数的日記 を含むブックマーク はてなブックマーク - omakeをocaml-3.12.1でビルドする - yoshihiro503の関数的日記

ocamlを使っている人はomakeをソースからビルドしてインストールすることができる。インストール方法は次の3ステップだ。

 $ make bootstrap
 $ make all
 # make install

しかし、ocaml-3.12.1 を使っている人がomake-0.9.8.6-0.rc1とかをソースからインストールしようとしたら、make all の段階で次のようなエラーが出る。

+ ocamlopt.opt -warn-error A -w Aez -I . -c lm_debug.ml
File "lm_debug.ml", line 176, characters 6-45:
Warning 9: the following labels are not bound in this record pattern:
info_info
Either bind these labels explicitly or add `; _' to the pattern.
File "lm_debug.ml", line 192, characters 13-53:                ] 00830 / 01203
Warning 9: the following labels are not bound in this record pattern:
info_info
Either bind these labels explicitly or add `; _' to the pattern.
File "lm_debug.ml", line 230, characters 16-61:
Warning 9: the following labels are not bound in this record pattern:
info_name
Either bind these labels explicitly or add `; _' to the pattern.
File "lm_debug.ml", line 1, characters 0-1:                                    
Error: Error-enabled warnings (3 occurrences)
*** omake: 834/1203 targets are up to date                                     
*** omake: failed (20.28 sec, 218/218 scans, 165/188 rules, 454/1637 digests)
*** omake: targets were not rebuilt because of errors:                         
   src/libmojave/lm_debug.cmx
   src/libmojave/lm_debug.o

この原因はomakeのビルドにおいてほとんどの警告をコンパイルエラーにしている設定と、OCamlがversion-3.12になったときに微妙にrecordに関する警告をキツくしたことである。

この問題はプロジェクトルートにあるOMakefileを編集してOCAMLFLAGSの設定を変えれば、解決する。

OCAMLFLAGS[] += -w Ae$(if $(OCAML_ACCEPTS_Z_WARNING), z)

↓ この行をつぎのように書き換える

OCAMLFLAGS[] += -w ae$(if $(OCAML_ACCEPTS_Z_WARNING), z)

2012-02-14 (Tue)

Javaでブロック式

| 21:27 | Javaでブロック式 - yoshihiro503の関数的日記 を含むブックマーク はてなブックマーク - Javaでブロック式 - yoshihiro503の関数的日記

Javaで2つのコンストラクタを定義して、一方から他方を呼びたいときにはthis(a,b,c)とアクセスするが、必ず最初に書かなければならないという制約がある。こういうときにScalaOCamlのようなlet式 or ブロック式が欲しくなる。

例えば次の例を見てほしい。

class C {
  public C(Person p) {
    ...
  }

  public C() { //こう書きたい
    Person imai = new Person("imai"); //デフォルトのPerson
    imai.setCurrentAddress("名古屋"); //現在の住所は"名古屋"
    this(imai); //ここでコンパイルエラー (Constructor call must be the first statement in a constructor)
  }
}

こういうときScalaならブロックを使って次のようにかける。

class C {
  def this(p : Person) = {
    ...
  }

  def this() = {
    this {
      Person imai = new Person("imai");
      imai.setCurrentAddress("名古屋");
      imai
    }
  }
}

Javaでも同じように書くためにBlockというinterfaceを導入してみた。

interface Block<A> { A body(); }

使い方:

class C {
  public C(Person p) {
    ...
  }

  public C() {
    this(new Block<Person>() {
      @Override public Person body() {
        Person imai = new Person("imai"); //デフォルトのPerson
        imai.setCurrentAddress("名古屋"); //現在の住所は"名古屋"
        return imai;
      }
    }.body());
  }
}

うーん...

2011-11-03 (Thu)

社内プログラミングコンテスト - Excel列名変換問題 - をScalaで解いてみた

| 12:14 | 社内プログラミングコンテスト - Excel列名変換問題 - をScalaで解いてみた - yoshihiro503の関数的日記 を含むブックマーク はてなブックマーク - 社内プログラミングコンテスト - Excel列名変換問題 - をScalaで解いてみた - yoshihiro503の関数的日記

Scala練習中なので、Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編) - give IT a try の問題をScalaを使って解いてみた。

ディレクトリ構成はこんな感じ。

src/
 |- main/scala/Main.scala
 `- test/scala/Test.scala

まず、ブログ記事にあったperlでのテストコードを元にScalaTestを使ったScalaのテストコードを用意した。

ブログ記事にはto_nのテストしか無かったが、of_nも同様に付け足した。テストファースト大事。

import org.scalatest.FunSuite
import Main._

class Test extends FunSuite {
  test("to_n") {
    assert(to_n("A") === 1)
    assert(to_n("B") === 2)
    assert(to_n("C") === 3)
    assert(to_n("Y") === 25)
    assert(to_n("Z") === 26)
    assert(to_n("AA") === 27)
    assert(to_n("AB") === 28)
    assert(to_n("AY") === 51)
    assert(to_n("AZ") === 52)
    assert(to_n("BA") === 53)
    assert(to_n("BB") === 54)
    assert(to_n("IV") === 256)
    assert(to_n("XFD") === 16384)
  }

  test("of_n") {
    assert("A" === of_n(1))
    assert("B" === of_n(2))
    assert("C" === of_n(3))
    assert("Y" === of_n(25))
    assert("Z" === of_n(26))
    assert("AA" === of_n(27))
    assert("AB" === of_n(28))
    assert("AY" === of_n(51))
    assert("AZ" === of_n(52))
    assert("BA" === of_n(53))
    assert("BB" === of_n(54))
    assert("IV" === of_n(256))
    assert("XFD" === of_n(16384))
  }
}

実装はこんな感じ。Haskellならもっと簡潔にかけるかも。

object Main {
  def main(args : Array[String]) = {
    val mode = args(0).toInt
    val input = args(1)
    mode match {
      case 0 =>
        println(to_n(input))
      case 1 =>
        println(of_n(input.toInt))
    }
  }

  def to_n(input : String) : Int = {
    def alp_to_n(alp : Char) = (alp.toInt - 'A'.toInt + 1)
    input.map(alp_to_n).foldLeft(0) {
      case (store, n) => store * 26 + n
    }
  }

  def of_n(input : Int) = {
    def n_to_alp(n : Int) = ('A' + n - 1).toChar
    def iter(x : Int) : List[Int] =
      (x%26, x/26) match {
        case (0, 0) => Nil
        case (0, r) => 26 :: iter(r-1)
        case (a, r) => a :: iter(r)
      }
    iter(input).map(n_to_alp).reverse.mkString
  }
}

HinesHines2011/12/11 21:32Real brain power on display. Thanks for that anwser!

2011-10-15 (Sat)

Androidを簡易的な対話サーバーにしよう

| 21:23 |  Androidを簡易的な対話サーバーにしよう - yoshihiro503の関数的日記 を含むブックマーク はてなブックマーク -  Androidを簡易的な対話サーバーにしよう - yoshihiro503の関数的日記

実装

java.net.ServerSocketを使えば簡単なソケットサーバーをAndroidアプリとして実装できる。

今回はScalaを使って純粋関数的な一行ずつ読む対話的サーバーを実装してみた。Scalaを使うことによって驚くほど簡単に実装でき、かつ単体テストが細かくできるようになる。サーバーのロジック部分は次のような入力行の無限列(Iterator[String])をもらって応答の列(Iterator[String])を返すという純粋関数で記述した。ロジック部分とサーバーとしての入出力を切り分けることによって、ロジックだけの単体テストが非常に簡単になる。

object Interpreter {
  def lineInterpreter(lines : Iterator[String]): Iterator[String] = {
    lines takeWhile {
      //exitが来たら応答はおしまい
      line => line != "exit"
    } filter {
      //空行は無視
      line => line != ""
    } map {
      //入力に対してFoo:をくっつけるだけの簡単なお仕事
      line => "FOO: "+line
    }
  }
}

これを使う側は次のような感じ。

  def server() = {
    import java.net.ServerSocket
    import java.net.Socket
    import java.io._
    def using[T](op : (InputStream,OutputStream) => T) : T = {
      val server = new ServerSocket(port) {
        setSoTimeout(0)
      }
      val socket = server.accept()
      val in =  socket.getInputStream()
      val out = socket.getOutputStream()
      def close() = { server.close(); in.close(); out.close() }
      try {
        val y = op(in, out)
        close(); y
      } catch {
        case (e: Exception) => { close(); throw e }
      }
    }
    using {
      case(in, out) =>
        val writer = new PrintWriter(out)
        Interpreter.lineInterpreter(getLines(in)).foreach {
      	  res =>
	    writer.println(res)
	    writer.flush()
        }
    }
  }

  def getLines(in : java.io.InputStream) : Iterator[String] = {
    import java.io._
    val reader = new BufferedReader(new InputStreamReader(in))
    Iterator.continually(reader.readLine()).takeWhile(_ != null)
  }

getLines関数でjavaのInputStreamを行の遅延リストにしているのがポイントだ。この技は@ScalaTohokuさんに教えてもらった。

あと、スレッドを作る必要があったのでActorも少しだけ使ってみた。

ソースコードを含むプロジェクトはこちら:

https://github.com/yoshihiro503/AndroidSocketServer

使い方

APKファイルを以下からダウンロードしてAndroidに入れる。

https://github.com/yoshihiro503/AndroidSocketServer/downloads

Android側で起動するとIPアドレスが表示され、サーバーが起動するので別の端末からncコマンドなどでアクセス。

 $ nc [AndroidのIPアドレス] 8080

入力した行に対してFoo: がくっついていたら成功!空行は無視され、終了する場合はexitと入力する。

ビルド

自分でビルドしてみたいという人はsbt-0.11.0とSBTのandroid-pluginを用意しよう。それらがあればビルドや実行は非常に簡単だ。

 $ sbt android:package-debug

実機にインストール

 $ adb install -r target/socketserver-0.1.apk

2011-08-04 (Thu)

ocamltter 1.6.0リリース

| 20:05 | ocamltter 1.6.0リリース - yoshihiro503の関数的日記 を含むブックマーク はてなブックマーク - ocamltter 1.6.0リリース - yoshihiro503の関数的日記

実験的におしゃべり機能を付けてみました。config.mlのtalkをtrueにすることで有効になります。mpg123などのmp3再生コマンドが必要です。

https://github.com/yoshihiro503/ocamltter/downloads

cmoncmon2011/08/05 19:32ツイッターのユーザー名とパスワードはどこで設定するのでしょうか?

yoshihiro503yoshihiro5032011/10/03 13:55oauthを使っており、ユーザー名、パスワードを直接設定する必要はありません。初回起動時に表示されるURLにしたがってPINを入力してみてください。

DaveighDaveigh2011/12/11 14:59Thanks for wrtiing such an easy-to-understand article on this topic.

2011-04-06 (Wed)

haXe用のQuickCheckを書いてみた。

| 19:31 | haXe用のQuickCheckを書いてみた。 - yoshihiro503の関数的日記 を含むブックマーク はてなブックマーク - haXe用のQuickCheckを書いてみた。 - yoshihiro503の関数的日記

使い方はこんな感じ

import haxe.unit.TestCase;
import haxe.unit.TestRunner;
import QuickCheck;

class MyTest extends QuickCheck {
  public function testN() {
    var q = this;
    quickCheck(forall(Gen.int, function(n) {
      return q.check(2*n == n+n);
    }));
  }

  public function testAppendLength() {
    var q = this;
    quickCheck(forall(Gen.list(Gen.int), function(xs) {
      return q.forall(Gen.list(Gen.int), function(ys) {
        return q.check(xs.length + ys.length == Lambda.concat(xs,ys).length);
      });
    }));
  }
}

class Sample {
  static function main() {
    var r = new TestRunner();
    r.add(new MyTest());
    r.run();
  }
}

ソースコードはこちら

https://bitbucket.org/yoshihiro503/haxecheck

2011-03-06 (Sun)

Scalaでパイプライン演算子

| 16:08 | Scalaでパイプライン演算子 - yoshihiro503の関数的日記 を含むブックマーク はてなブックマーク - Scalaでパイプライン演算子 - yoshihiro503の関数的日記

Scalaを書いているとF#のようなパイプライン演算子が欲しくなることがよくあります。例えば次のような場合です。

欲しくなる例1: 整数とかのパースをOptionで安全に行いたいケース

Scala標準の String#toInt は失敗した時に例外を発生してしまいますが、例外ではなくOptionで扱いたくなりました。こんなときはきっと次のような関数を定義しますよね。

def parseInt(s: String): Option[Int] =
  try {
    Some(s.toInt)
  } catch {
    case _ => None
  }

でもこれは関数であって文字列のメンバではないので、この関数を使用するときに処理の流れがわかりにくくなることがあります。

例えば「ファイルから一行読んで → 整数にパースして → 失敗した場合は0とする」という一連の処理は以下のようになってしまいます。処理の流れとプログラムが微妙に合ってないですね。

parseInt(file.getLine()) getOrElse(0)

ここでパイプライン演算子を使うと以下のように書くことができます。

file getLine() |> parseInt getOrElse(0)

実行したい処理の順番に関数を記述できてハッピーになりました。

欲しくなる例2: Intの配列に対して平均をとるaverage操作を定義したい。

Array[Int]型の値に対して平均値をとる操作が頻繁に現れるプログラムを書いていたとします。

このとき次の関数を定義して使うとします。

def average(xs: Array[Int]): Double = xs.foldLeft(0.0)(_+_) / xs.length

ここまでは普通で自然だと思いますが、これを使うときにパイプライン演算子を使うとまるでaverageがArray[Int]のメンバであるかのように自然に処理を連ねることができます。

Array(1,2,5,5,9,0) |> average floor

種明かし(パイプライン演算子の定義)

このようなパイプライン演算子は実は次のように定義できます。

クラス名や関数名はテキトーです。すいません。

class A[T](val v: T) { def |>[S] (f: T => S): S = f(v)}
implicit def AofT[T] (x: T): A[T] = new A(x)

みなさんもぜひ使って見てください。楽しくね!

[追記: 2011 03/07]

Scala練習場でも使って見ました。

http://nagoyascala.appspot.com/bbs/thread/52001

あと、id:osiireさんに言われたのですが、これだとAクラスというクラス名が予約されてしまうので、次のように一行で書いた方がより実用的かもしれません。

implicit def AofT[T](x: T) = new { def |>[S] (f: T => S): S = f(x) }

2011-01-30 (Sun)

算数オリンピックの問題をリストモナドで解く

| 19:53 | 算数オリンピックの問題をリストモナドで解く - yoshihiro503の関数的日記 を含むブックマーク はてなブックマーク - 算数オリンピックの問題をリストモナドで解く - yoshihiro503の関数的日記

はてブニュースで話題のあった算数オリンピックの問題をリストモナドで解いてみた。

5×5のマス目に6個の○を次の条件を満たすように書きます。

条件:各行・各列に少なくとも1個は○を書く。同じマスには2個以上の○を書くことはできない。

このとき、6個の○を書く方法は全部で何通りありますか。

http://b.hatena.ne.jp/articles/201101/2261

Haskellはあまり経験が無いのでよくわからない。こんな感じ?

import Control.Monad
type Point = (Int, Int)

main :: IO ()
main = print $ length sol

stone :: [Point]
stone = do
   x <- [1..5]
   y <- [1..5]
   return (x,y)

sol :: [[Point]]
sol = do
  p1 <- stone
  p2 <- stone
  guard (p1 < p2)
  p3 <- stone
  guard (p2 < p3)
  p4 <- stone
  guard (p3 < p4)
  p5 <- stone
  guard (p4 < p5)
  p6 <- stone
  guard (p5 < p6)
  let ps = [p1,p2,p3,p4,p5,p6]
  guard $ all (\i -> any (\(x,_) -> x == i) ps) [1..5]
            && all (\j -> any (\(_,y) -> y == j) ps) [1..5]
  return ps

実行結果

4200

real    0m0.345s
user    0m0.333s
sys     0m0.008s