読者です 読者をやめる 読者になる 読者になる

webネタ

技術系ブログ。web系SE。1988。♂。@rhong_

scalaでコマンド叩くときのエラー

参考資料

エラー制御

エラーが2パターンある。

1、 コマンド自体実行できないと例外(IOException)が飛ぶ

scala> Process("foo").!
java.io.IOException: Cannot run program "foo": error=2, No such file or directory
  at java.lang.ProcessBuilder.start(ProcessBuilder.java:1041)
  at scala.sys.process.ProcessBuilderImpl$Simple.run(ProcessBuilderImpl.scala:69)
  at scala.sys.process.ProcessBuilderImpl$AbstractBuilder.run(ProcessBuilderImpl.scala:98)
  at scala.sys.process.ProcessBuilderImpl$AbstractBuilder.run(ProcessBuilderImpl.scala:97)
  ... 33 elided
Caused by: java.io.IOException: error=2, No such file or directory
  at java.lang.UNIXProcess.forkAndExec(Native Method)
  at java.lang.UNIXProcess.<init>(UNIXProcess.java:135)
  at java.lang.ProcessImpl.start(ProcessImpl.java:130)
  at java.lang.ProcessBuilder.start(ProcessBuilder.java:1022)
  ... 36 more

2、 コマンド実行後の失敗は例外飛ばず、デフォルトだと標準エラー出力

scala> Process("ls /foo").!
ls: /foo: No such file or directory
res11: Int = 1

例外のほうはTryでなんかすればいいとして、

標準エラー出力をロギングしたいときは、ProcessLoggerを使う。

def plogger = ProcessLogger(identity, logger.error(_))
Process("ls /foo") ! plogger

すごいHaskell本のメモ

FunctorやらMonadまわりのメモ。

Functor

箱と比喩されている。リストやMaybeなど大体Functor。

fmapをもってる。(Scalaでいうmap)

fmap :: Functor f => (a -> b) -> f a -> f b

aをとりbを返す関数と、Functor値aをとる。aに関数を適用してFunctor値bを返す。

Prelude> fmap (+1) (Just 1)
Just 2

しかしFunctorだと中の関数に何かしようとすると辛くなる。

Prelude> let a = fmap (+) (Just 1)

Prelude> :t a
a :: Maybe (Integer -> Integer) 部分適用(カリー)されてMaybeの中に関数が入っている

Prelude> fmap (\f -> f 1) a
Just 2

これを簡単にできるのが↓

Applicative

文脈と比喩されている。リストやMaybeもまたApplicative。

pureと<*>を持っている。

Prelude> import Control.Applicative

pureは、aを受け取りapplicative aを返すだけの関数。

Prelude Control.Applicative> :t pure
pure :: Applicative f => a -> f a

<*>は

Prelude Control.Applicative> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

Appricativeに入った関数を受け取るところがFunctorと違う。

Prelude Control.Applicative> Just (+1) <*> Just 1
Just 2

functorで辛かった部分をapplicativeで書くと、

Prelude Control.Applicative> fmap (+) (Just 1) <*> Just 1
Just 2

短く書くこともできる↓

Prelude Control.Applicative> (+) <$> (Just 1) <*> Just 1
Just 2

簡単に書けた。

関数のApplicative

関数の<*>は、

f <*> g = \x -> f x (g x) : fとgの関数を受け取り、g xした結果とxをfに渡す関数を返す

Prelude Control.Applicative> (+) <$> (+1) <*> (+1) $ 1
4

??

(+) <$> (+1) = (1 + a) + b
(+) <$> (+1) <*> (+1) = \x -> (1 + x) + (1 + x)
これに1を渡すと、4

たしかに4だ。

リストのApplicative

Prelude Control.Applicative> (+) <$> [1,2,3] <*> [2,3,4]
[3,4,5,4,5,6,5,6,7]

それぞれに適用される。

ペアで適用したいときは、

Prelude Control.Applicative> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [2,3,4]
[3,5,7]

liftA2

Prelude Control.Applicative> :t liftA2
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c

aとbをとりcを返す関数と、Applicative値aとApplicative値bをとり、Applicative値cを返す関数。

Prelude Control.Applicative> liftA2 (+) (Just 1) (Just 2)
Just 3

↓より↑のほうが分かりやすい。

Prelude Control.Applicative> (+) <$> (Just 1) <*> (Just 2)
Just 3

Monoid

Monadとは別物。

結合法則

ok : 1 + (2 + 3) == (1 + 2) + 3 (++や*など)
ng : 1 - (2 - 3) == (1 - 2) - 3
みたいに順番変わってもokなもの

単位元

リストなら[]。(+)なら0。など

Prelude> import Data.Monoid
Prelude Data.Monoid> [1,2] `mappend` [3,4]
[1,2,3,4]
Prelude Data.Monoid> "ab" `mappend` "cd"
"abcd"

その他いろいろ

-- Sum, Product
Prelude Data.Monoid> (Sum 1) `mappend` (Sum 2)
Sum {getSum = 3}
Prelude Data.Monoid> (Product 2) `mappend` (Product 2)
Product {getProduct = 4}
Prelude Data.Monoid> getProduct $ (Product 2) `mappend` (Product 2)
4

-- Any, All
Prelude Data.Monoid> (Any True) `mappend` (Any False)
Any {getAny = True}
Prelude Data.Monoid> (All True) `mappend` (All False)
All {getAll = False}

-- compareは左側がEQのときに右側を評価
Prelude Data.Monoid> LT `mappend` GT
LT
Prelude Data.Monoid> EQ `mappend` GT
GT

Foldable

畳み込むアレ

Prelude Data.Monoid> import Data.Foldable as F

Prelude Data.Monoid F> :t F.foldl
F.foldl :: Foldable t => (a -> b -> a) -> a -> t b -> a

Prelude Data.Monoid F> F.foldl (+) 0 [1,2,3]
6

Prelude Data.Monoid F> F.foldl (+) 1 (Just 1)
2

Monad

噂のアレ

Applicativeのupgrade版

-- pureと同じようにreturnがある
Prelude> :t return
return :: Monad m => a -> m a

-- バインドと呼ばれる。scalaでいうflatMap。
Prelude> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b

Prelude> Just 1 >>= \x -> Just (x + 1)
Just 2

Prelude> [1,2,3] >>= \x -> [x + 1]
[2,3,4]

Prelude> [1,2,3] >>= \x -> if x > 1 then [x] else []
[2,3]

Prelude> Just 1 >>= \x -> Just (x+x) >>= \x -> Just (x*x) >>= \x -> if x > 10 then Just x else Nothing
Nothing

-- Scalaだと
scala> Some(1) flatMap { x => Some(x+x) } flatMap { x => Some(x*x) } flatMap { x => if (x > 10) Some(x) else None }
res6: Option[Int] = None

『あーScalaのアレと同じか』というのがちょいちょいあって助かる。

rubyでサムネイル作成

画像を指定のサイズでうまいことリサイズし、余白を埋め、透かしをいれる。

require 'RMagick'

file = ARGV[0]

def main(file, w, h)
  img = Magick::Image.read(file).first
  filename = img.filename

  img = watermark resize img, w, h
  img.write "dest/#{filename}"
end

def resize(img, w, h)
  img = img.resize_to_fit!(w, h)
  bg = Magick::Image.new(w, h) do self.background_color = 'white' end
  bg.composite!(img, Magick::CenterGravity, Magick::OverCompositeOp)
end

def watermark(img)
  mark = Magick::Image.read('./watermark.png').first
  img.composite!(mark, Magick::SouthWestGravity, Magick::OverCompositeOp)
end

main file, 100, 100

phpのアレと違い、rubyでの画像加工は直感的に書けていい。

TypeScriptでScalaのOptionを

書いてみた。

使い方↓

Optional(x).map(i => i + 100).getOrElse(0)
Optional(x).exists(i => i % 2 == 0)

(Optionは予約されてて使えなかったのでOptionalに)

もうほとんどScalaじゃ...!

TryとかEitherならすぐ書けそうだけどリスト周り書き出すとしんどそう...

TypeScript結構いいっぽい。coffeeも試してみたいけど型が...

class OptionT<A> {
  isEmpty: boolean
  get: () => A
  map<B>(f: (a: A) => B): OptionT<B> {
    return this.isEmpty ? new None : new Some(f(this.get()))
  }
  flatMap<B>(f: (a: A) => OptionT<B>): OptionT<B> {
    return this.isEmpty ? new None : f(this.get())
  }
  getOrElse<B extends A>(d: B): A {
    return this.isEmpty ? d : this.get()
  }
  filter(f: (a: A) => boolean): OptionT<A> {
    return (this.isEmpty || f(this.get())) ? this : new None 
  }
  exists(f: (a: A) => boolean): boolean {
    return !this.isEmpty && f(this.get())
  }
}

class Some<A> extends OptionT<A> {
  isEmpty = false
  get = () => { return this.value }
  constructor(private value: A) { super() }
}

class None extends OptionT<any> {
  isEmpty = true
  get: () => any = () => { throw ""; "" }
}

var Optional = <A>(value: A) => {
  return value ? new Some<A>(value) : new None
}

メソッド全然たりてないけど...まぁ。Noneが残念な感じになってる...

てきとうにテスト

var a = Optional(1).map(v => v + 100)
console.log(a) // Some(101)

var b = Optional(0).map(v => v + 100)
console.log(b) // None

var c = Optional(1).flatMap(v => Optional(v)).map(v => v + 1)
console.log(c) // Some(2)

var d = Optional(1).filter(i => i % 2 == 0).getOrElse(0)
console.log(d) // 0

var e = Optional(1).exists(i => i % 2 == 0)
console.log(e) // false

JavaScriptでもモナドを!

require.jsの、requireとdefineの違い

require.jsのことがよく分かってなかったので、コード読んで調べてみた。

require と define

  • requireは、名前の通り実行すると、ファイルを読み込む。
  • defineは、名前の通り定義をすると、キューに突っ込まれるだけで読み込みはしない。

requireを実行すると、<scirpt>でファイルを読み込む。

読み込んだファイル内にはdefine([‘foo/bar’], function() { .. }) とか書いてるのでdefineされてるものはキューに突っ込まれる。

require完了後setTimeout(4ms)したらキューを処理するので、defineしたものも読み込まれる。

読み込んだものは、グローバルなオブジェクトで管理されて同じものは読み込まれないようになっている。

foo.js

define([‘bar1’, 'fuga2'], function() { … })

bar1.js

define([‘piyo3’], function() { … }) 

があるとして、

require(“foo”)

を実行する。

fooが読み込まれ、defineのものがキューに入る。[bar1, fuga2]

キューが処理される。最初はbar1。bar1にはpiyo3がdefineされているので、またキューに入る。[fuga2, piyo3]

キューがなくなるまで読み込み続ける。

幅優先探索のようになっている。

git cherry-pickで複数まとめて取り込む

masterに緊急でコミットしてしまって、後で開発ブランチと整合性合わせたいみたいな状況(仮)

差分を確認します。

$ git cherry -v develop master
84a9532902724a2c26c746aae2505bc60e66e7cb 緊急対応。index.html修正
9e630b327fa7879562bff8b3772f36e6a0e74ca9 緊急対応2。typo..
eb8fa59260d7f304836a790a298008fedf00f2d3 緊急3。リンク切れ対応

取り込みます。

$ for cid in $(git cherry -v develop master | awk '{print $2}'); git cherry-pick $cid
[develop 0861381] 緊急対応。index.html修正
 1 file changed, 1 insertion(+), 1 deletion(-)
[develop 967988d] 緊急対応2。typo..
 1 file changed, 1 insertion(+), 1 deletion(-)
[develop 8aee8b7] 緊急3。リンク切れ対応
 1 file changed, 1 insertion(+), 1 deletion(-)

何がいいたいかと言うと、cherry-pickでうまくまとめて取り込めなかったので、頑張らずにforでいいじゃないかと。

CoffeScriptをさらに書きやすくしたLiveScript

LiveScriptは、CoffeScriptをもっとよくしたCocoのfork。

LiveScriptで書かれたpreludelsというライブラリがあるので関数型っぽい関数が使える。(foldやらscanやら)

Function

カリー化

f = (x, y) --> x * y
f(2)(2) # 4

f = (x) -> (y) -> x * y
f(2)(2) # 4

合成

f = (x) -> x + 1
g = (y) -> y + 2
(f << g)(1) # 4
(f . g)(1)  # 4

(f >> g)(1) # 4

List

パイプ

[1 to 10]
  |> map (i) -> i * i
  |> reverse
  |> head
# 100

リスト内包

[ x + y for x to 3  when x % 2 is 0
        for y to 3
]
# [0,1,2,3,2,3,4,5]

etc..

zip [1 to 3] [\A to \C] # [[1,"A"],[2,"B"],[3,"C"]]
scan (+), 0, [1 to 3]   # [0,1,3,6]
fold (+), 0, [1 to 5]   # 15
take-while odd, [1 3 5 6] # [1 3 5]

定義

obj =
  * name: 'tessa'
    age:  23
# {"name":"tessa","age":23}

<[ a b c ]>
# ["a", "b", "c"]

未実装

f = -> ...
f() # throw Error('unimplemented');

パターンマッチ

f = -> switch it
  | 0 false null undefined => "ng"
  | "foo" => "bar"
  | _ => "ok"
f(0) # ng
f(1) # ok

正規表現

/^[a-z]*/ == "abc123" # [abc]

再帰

sum

sum = (list, acum = 0) ->
  | !list.length => acum
  | _ =>
    [head, ...tail] = list
    sum(tail, acum + head)
sum([1,2,3]) # 6

qsort

qsort = (list) ->
  | !list => []
  | list.length is 1 => [head list]
  | _ =>
    h = head list
    {l, c, r} = group-by ((i) ->
      | h > i => "l"
      | h == i => "c"
      | h < i => "r"), list
    qsort(l) ++ c ++ qsort(r)
qsort([3,1,2,4,5,-1,-1,2]) # [-1,-1,1,2,2,3,4,5]

だいぶ短く書けるようだ。

しかしheadとtailが書きづらい。

まとめ

これがJSだと思うとだいぶいいと思う。

coffescriptよりもさらに短く書けて、cocoのforkなのでより最適化されている。というかパターンマッチが書きやすい!

関数型やってる人にはいいかもしれない。(・・`)