SE情報技術研究会’s blog

http://se-info-tech.connpass.com

2015-12-30 『ふつうのHaskellプログラミング』 もくもく読書会(第8章) の振り返り

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

  • 作者: 青木峰郎,山下伸夫
  • 出版社/メーカー: SBクリエイティブ
  • 発売日: 2014/10/05
  • メディア: オンデマンド (ペーパーバック)
  • この商品を含むブログを見る

第8章 関数

値としての関数

関数束縛

  • 関数名を変数とし、その引数及び処理の定義の関数を束縛すること

例:

square n = n * n
  • 変数:square
  • 関数:n = n * n

高階関数

  • 関数を引数として受け取ったり、戻り値として返したりする関数

無名関数

  • 変数を束縛しない関数
  • 最初の「\」をラムダと言う
  • 引数は複数設定可
\引数1 引数2->

例:

\ x y -> x + y

無名関数のパターンマッチ

  • 無名関数の引数でもパターンマッチが使える
  • 無名関数で使えるパターンは一つだけ

関数合成

  • 複数の関数を合わせて新しい関数を作ること
  • 「.」を使って2つの関数を合成できる
numberOfLines :: String -> Int
numberOfLines cs = length $ lines cs

は、関数合成を使うと下記に書き換えれる

numberOfLines :: String -> Int
numberOfLines = length . lines

(.)関数の定義

(.) :: (b -> a) -> (a -> b) -> (a -> c)
  • (f . g) にxを与えた(f . g)xと、 f(g(x))は同じことを意味する
コメント
numberOfLines cs = length $ lines cs

numberOfLines = length . lines

で、なぜ関数合成のほうは引数「cs」がなくていいのか理由がわからない

引数「cs」が不要なら

numberOfLines = length $ lines

でも、良さそうな気がするが、これだとコンパイルエラーになる

それにその前にでてきた関数束縛と無名関数の説明に従うと

numberOfLines = \() -> length . lines

となるのでは? ただ、これはコンパイルエラーになるし、そもそも引数なしで処理ができるはずないからおかしい。

など考えていたが「部分適用」のとこで説明があった

部分適用

  • 引数を一度にすべて渡さず、一部だけ渡しておくこと
  • Haskellでは複数の引数を取る関数に1つだけ引数を渡してもエラーにはならない
  • Haskellでは真の意味で複数の引数を取る関数はない
    → 1つの引数を取り他の引数を取る関数を返す高階関数とみなすことができる
  • セクション:部分適用を使い引数をひとつだけ渡した二項演算子
  • 「-」は(-1)でも(- 1)でも数値「-1」とみなされセクションをつくることはできない
  • (subtract 1)で1を引くセクションができる
コメント

関数合成の

numberOfLines :: String -> Int
numberOfLines = length . lines

ではlength . linesがセクションとなり引数を省略できるのはわかった。

関数束縛と無名関数で

numberOfLines = length . lines = \cs -> length . lines cs

と言うことなのだろう。

ただ、そうなると

numberOfLines = length $ lines

これがダメな理由がわからない。 length $ linesもセクションと見れるのでは?

コメントより

($) :: (a -> b) -> a -> b
(.) :: (b -> c) -> (a -> b) -> a -> c

型から見ると($)は、最初の引数が、関数を示す(a -> b)であり 最後の引数が、最初の引数の関数の引数の型であるa型であるような、二項演算子となる。

と言うことで($)の場合、lengthが(Foldable t => t a -> Int)だがlinesはa型を持つFoldableでないといけないが関数なので、引数の省略は不可。

> :t length
length :: Foldable t => t a -> Int

ポイントフリースタイル

  • ポイントフリースタイルとは、関数を関数で定義するコーディングスタイル
  • 「point」は圏論の用語でHaskellでいう値を示す
  • ポイントフリースタイルの場合、型宣言はつけるべき
    → 引数がないので、コードが非常にわかりづらくなる

練習問題

  • 空白かどうかについて独自に関数を定義していたがimport Data.CharisSpaceと言う関数があった
  • 問題にでてくる文字列はここでは読み込んだファイルの複数行の文字列の意味ではなく一行の文字列のこと

※ 最初に文字列がファイルから読み込んだ文字列のことかと思い、下記のように書いていた

lstrip = unlines . (fmap (dropWhile isSpace)) . lines

でハマったのが

lstrip = unlines $ (fmap (dropWhile isSpace)) . lines

unlines の後の「$」に気づいていなくてエラーがでていたこと。 「$」演算子があるとポイントフリースタイルが使えないのは何故?

これも上記同様に($)の定義が下記であるため

($) :: (a -> b) -> a -> b

2015-12-29 『ふつうのHaskellプログラミング』 もくもく読書会(第7章) の振り返り

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

  • 作者: 青木峰郎,山下伸夫
  • 出版社/メーカー: SBクリエイティブ
  • 発売日: 2014/10/05
  • メディア: オンデマンド (ペーパーバック)
  • この商品を含むブログを見る

第7章 基本的な構文

コメント

1行コメント

  • -- コメント
  • -演算子にも使えるので--の後に記号を入れるとコメントとして解釈されない
  • 通常は--の後の空白を入れる
square n = n * n -- nの2乗

ブロック形式のコメント

  • {-- 複数行のコメント --}
  • コメントのネストも可能
{--
関数をコメントアウト
square n = n * n {-- nの2乗 --}
--}

リテレイト形式

  • リテレイト形式とはコメントの中にコードを描く形式
  • 慣例的に拡張子は「.lhs」※ 「.hs」だとエラーになる
  • 先頭に「>」をつけた行だけがコードになる
  • \begin{code}から\end{code}で囲む形式もある
  • 「>」か\begin{code}から\end{code}のどちらかしかダメなもよう

下記はコンパイルは通る

> main = print $ square 5

関数 square は数値の2乗をあらわす

> square :: Int -> Int
> square n = (n * n)

下記のコードはコンパイルエラーを起こす

\begin{code}
main = print $ square 5
\end{code}

関数 square は数値の2乗をあらわす

> square :: Int -> Int
> square n = (n * n)

実行すると…

>ghc literateNG.lhs
[1 of 1] Compiling Main             ( literateNG.lhs, literateNG.o )

literateNG.lhs:8:12: parse error on input ‘=’

レイアウト

ブレース構文

  • ;で式を区切り{}で囲むことでコードブロックを表現できる

例えば、インデントを使ったdo式を

main = do cs <- getContents
          putStr cs

下記に書き換えることが可能

main = do 
{
    cs <- getContents;
putStr cs;
}

レイアウト

  • 複数の式の桁をそろえることで式をまとめる方式をレイアウトもしくはオフサイドルールという
  • 束ねたい式のインデントをそろえると、それらが1つのコードブロックに属すると解釈される
  • ただし行頭には書けない ※ 最低1文字の空白が必要

do式は下記のように書ける

main = do
  cs <- getContents
  putStr cs
  • インデントより右側なら前の行の続きとみなされる
main = do 
  cs <- 
    getContents
  putStr cs

そのため式の途中でインデントを揃えると別の式とみなされ粉ピルエラーになるもよう

main = do 
  cs <- 
  getContents
  putStr cs

※ 最低でも2行はインデント

コメント
  • 自動でインデントするエディタなら改行したほうがインデントを揃えやすい

    if式

  • 式1と式2は式でありコードブロックでないことに注意
if 条件式 then1 else2

パターンマッチ

  • 値のパターンの場合分けのこと
  • パターン分けと同時に値の一部の変数を束縛することもできる
  • 関数定義などで使われる
  • すべてのパターンマッチに失敗したエラー
  • ghc-Wのオプションをつけると実行時エラーが出る場合は警告が出る
  • パターンには具体的な値、タプル、リスト、データコンストラクタが使える
コメント

@(アズパターン:as-pattern)

  • パターンで使っている変数名とは別にパターン自体に変数名をつける方法
変数名(パターン)

例:

str@(c:cs)

とある場合、値に[1,2,3]だと

  • str = [1,2,3]
  • c = 1
  • cs = [2, 3]

ガード

  • 変数の値の条件によって処理する定義を分ける方法
  • ガードは|=の間のことをあらわす
  • 条件は複数設定でき、上から順に判定される
  • 最後に「otherwise」で全ての条件にあわない場合の処理を行うことが可能
関数名 変数 | 条件1 = 定義1
            | 条件2 = 定義2
            || otherwise = 定義…
  • パターンマッチと合わせて使うことも可能

case式

  • 関数の引数以外でパターンマッチやガードを使う方法
  • パターンやマッチに合った場合に実行される処理の定義は=ではなく->の右に記述する
  • case式のogの後に{}で囲う記述も可能
caseof 
  パターンA
    | ガード1 -> 定義A1
    | ガード2 -> 定義A2
  パターンB
    | ガード1 -> 定義B1
    | ガード2 -> 定義B2
  …

関数定義

  • 関数は引数を使って何かする変数とみなすこと可能(関数束縛)

関数や変数の識別子

使える文字

  • アルファベット(a~z、A~Z)
  • 数字(0~9)
  • _
  • '

規約

  • 1文字目は小文字か_ ※ ただし公式に_は避けるようにかいてある
  • 'は補助的な関数につけられることが多い
  • 予約語もあり、それは使えない

二項演算子

  • 引数と引数の間に記号を書くことで表現できる関数
  • 使える記号がいくつか用意され、独自に定義できる
  • 予約語もあり、それは使えない
  • 演算子をむやみに作るとコードが読みづらくなるので使う前に検討すべき
  • 識別子を使った通常の2引数とる関数も「`」で囲うことで二項演算子として使用できる
  • 逆に()で囲って二項演算子を前置形式で記述することも可能

優先順位と結合性

  • 優先順位には0~9があり、0が最低、9が最高
  • 左結合:1+1+1とある場合、(1+1)+1と左側の式から結合していく演算子
  • 右結合:x=y=1とある場合、x=(y=1)と右側の式から結合していく演算子
  • 非結合:x>y>zと結合することができない演算子
  • 独自で定義した演算子にも優先順位と結合性を定義できる

独自で定義した演算子の優先順位と結合性の設定

結合性 優先順位 演算子

結合性には下記がある

  • infixl:左結合
  • infixr:右結合
  • infixx:非結合

優先順位は省略可能(その場合デフォルトで9)

let式

  • その中でだけ有効な変数の定義(束縛の定義)方法
  • 式なのでそれ自体も値を持つ
  • 変数名が重複した場合、関数とlet式ならlet式の変数、ネストしたlet式ならネストした変数と、内側の変数が外側の変数を覆い隠す(シャドウイング
  • パターンを使って束縛できる
  • よく新しい変数を導入するために使われる

例:

f n = let x = n + 1
          y = n + 2
          z = n + 3
      in x * y * z

where節

  • 特定の定義内でだけ有効な変数の定義(束縛の定義)方法
  • 節なので値を持たない
  • 変数がガードにまたがって使える
  • 変数がパターンにまたがって使えない
  • パターンを使って束縛できる
  • よく関数内で使うための関数を定義するのに使われる

expandTab width cs = concatMap translate cs
  where
    translate '\t' = replicate width ' '
    translate c = [c]

練習問題

  • 特に問題なし

2015-12-29 『Learning Reactive Programming with Java 8』 もくもく読書会(Chapter 5)の振り返り

Learning Reactive Programming With Java 8

Learning Reactive Programming With Java 8

Chapter 5: Combinators, Conditionals, and Error Handling

Introduction

  • 多くのプログラムでは複数の違うソースからのデータをまとめて使うことがある
  • プログラムではこれらの違うソースからのデータ同士の依存関係は必要になることがある
  • エラーに対して対応できるプログラムは耐性が強いものである

この章で学ぶことは下記のことである

Combining the Observable Operator

※ Combinatorsはマーブルダイアグラム見たほうがわかりやすいので本で確認しならがら読んだほうが良い

The zip operator

    Observable<String> zip = Observable.zip( //
        Observable.from(Arrays.asList("Z", "I", "P", "P")), //
        Observable.interval(1L, TimeUnit.SECONDS), //
        (a, b) -> a + "/" + b);
    Helpers.subscribePrint(zip, "zip");

    Thread.sleep(5000L);

実行結果

zip:Z/0
zip:I/1
zip:P/2
zip:P/3
zip ended!
    Observable<String> zipWith = Observable
        .from(Arrays.asList("a", "b"))//
        .zipWith(Observable.interval(1L, TimeUnit.SECONDS), //
            (a, b) -> a + "/" + b);
    Helpers.subscribePrint(zipWith, "zip");

    Thread.sleep(5000L);

実行結果

zip:a/0
zip:b/1
zip ended!

The combineLatest operator

  • 複数のOservableインスタンスを要素を発行されるたびに組み合わせて処理をしていくメソッド
  • zipと違い要素の順ではなく処理時点のすれぞれの要素を使う
  • Oservableインスタンスの要素が揃う前に既に発行された要素は使われない
    Observable<String> combineLatest = Observable
        .combineLatest( //
            Observable
                .from(Arrays.asList("A", "B", "C", "D")), //
            Observable.interval(1L, TimeUnit.SECONDS), //
            (a, b) -> a + "/" + b);
    Helpers.subscribePrint(combineLatest, "combineLatest");

    Thread.sleep(5000L);

実行結果

combineLatest:D/0
combineLatest:D/1
combineLatest:D/2
combineLatest:D/3
combineLatest:D/4

上の例ではintervalメソッドのObservableインスタンスが最初の要素を発行する前にfromメソッドのObservableインスタンスは既にすべての要素を発行しているので、最後の要素は「D」になっている。

  • 本のサンプルも長いが十分わかりやすい

T The merge operator

  public static void main(String[] args)
      throws InterruptedException {
    Observable<String> alphabets = Observable
        .just("A", "B", "C", "D")
        .zipWith(Observable.interval(1L, TimeUnit.SECONDS), //
            MergeSample::onlyFirstArg);

    Observable<?> merge = Observable.merge(alphabets,
        Observable.interval(400L, TimeUnit.MILLISECONDS));

    Helpers.subscribePrint(merge, "merge");

    TimeUnit.SECONDS.sleep(5L);
  }

  static <T, R> T onlyFirstArg(T arg1, R arg2) {
    return arg1;
  }

実行結果

merge:0
merge:1
merge:A
merge:2
merge:3
merge:4
merge:B
merge:5
merge:6
merge:C
merge:7
merge:8
merge:9
merge:D
merge:10
merge:11

concat

startWith

The conditional operators

  • Observableインスタンスが他のObservableインスタンスが処理を行っているときにしか処理をしないなどのケースがある
  • コンディショナルなメソッドは特定の条件で実行するしないを管理することができる

The amb operator

コメント

The takeUntil(), takeWhile(), skipUntil(), and skipWhile() conditional operators

The takeUntil

  • 引数のObservableインスタンスが最初の要素を発行するまで処理をし続ける* 引数のObservableインスタンスが最初の要素を発行したら処理は終了する
    Observable<String> words = Observable
        .just("one", "way", "or", "another", "I'll",
            "learn", "Rxjava")
        .zipWith(Observable.interval(500L,
            TimeUnit.MILLISECONDS), (x, y) -> x);

    Observable<Long> interval = Observable.interval(2000L,
        TimeUnit.MILLISECONDS);

    Helpers.subscribePrint(words.takeUntil(interval),
        "takeUntil");

    TimeUnit.SECONDS.sleep(5L);

実行結果

takeUntil:one
takeUntil:way
takeUntil:or
takeUntil ended!

※ intervaleが2秒後に要素を発行するためwords.takeUntilの処理は終了する

takeWhile

  • 引数の関数がtrueを開けす限り処理を実行する
    Observable<String> words = Observable
        .just("one", "way", "or", "another", "I'll",
            "learn", "Rxjava")
        .zipWith(Observable.interval(500L,
            TimeUnit.MILLISECONDS), (x, y) -> x);

    Helpers.subscribePrint(
        words.takeWhile(word -> word.length() > 2),
        "takeWhile");

    TimeUnit.SECONDS.sleep(5L);

実行結果

takeWhile:one
takeWhile:way
takeWhile ended!

skipUntil

  • 引数のObservableインスタンスが最初の要素を発行するまで要素は発行しない
  • 引数のObservableインスタンスが最初の要素を発行したら元のObservableインスタンスの要素がそれまで発行していた分を除いて発行される
    Observable<String> words = Observable
        .just("one", "way", "or", "another", "I'll",
            "learn", "Rxjava")
        .zipWith(Observable.interval(500L,
            TimeUnit.MILLISECONDS), (x, y) -> x);

    Observable<Long> interval = Observable.interval(2000L,
        TimeUnit.MILLISECONDS);

    Helpers.subscribePrint(words.skipUntil(interval),
        "skipUntil");

    TimeUnit.SECONDS.sleep(5L);

実行結果

skipUntil:another
skipUntil:I'll
skipUntil:learn
skipUntil:Rxjava
skipUntil ended!

※ 最初の要素からではなく、それまで処理していたと思われるのを除いて、結果が発行される

コメント
  • xxxUntil系のメソッドは最初の要素が発行されたタイミングなので注意(Observableインスタンスの処理が完了するまでではない)

The defaultEmpty() operator

Handling errors

  • エラーが発生するとObservableインスタンスは処理のチェインを終了する
  • ただし、通常のプログラムのcatchブロックでの処理のように、エラー時に何かするロジックは実行可能

主なメソッドは3つに分類できる

  • return*
  • retry*
  • resume*

The return and resume operators

onErrorReturn

  • エラー発生時にOnErrorに行かずにonErrorReturnメソッドで処理を行い、Observableインスタスの処理を終了する

p.87のサンプル

誤:Observable<String> numbers = …
↓
正:Observable<Integer> numbers = …
    Observable<Integer> numbers = Observable
        .just("1", "2", "Three", "4") //
        .map(Integer::parseInt) //
        .onErrorReturn(e -> -1);
    Helpers.subscribePrint(numbers, "Error returned");

実行結果

Error returned:1
Error returned:2
Error returned:-1
Error returned ended!

onExceptionResumeNext

  • 例外発生時にOnErrorに行かずにonExceptionResumeNextメソッドの引数のObservableインスタンスの要素を使ってそれ以降の処理を行う
  • 対応するのはExceptionとそのサブクラスのみ
  • ErrorはonErrorに通知される
    Observable<Integer> numbers = Observable
        .just("1", "2", "Three", "4") //
        .map(Integer::parseInt) //
        .onExceptionResumeNext(
            Observable.just(50, 40, 30, 20, 10));
    Helpers.subscribePrint(numbers, "Exception resume");

実行結果

Exception resume:1
Exception resume:2
Exception resume:50
Exception resume:40
Exception resume:30
Exception resume:20
Exception resume:10
Exception resume ended!

onErrorResumeNext

その他の副作用オペレータ(side effect operator)

  • 要素の変更は行わない

主に次のものがある

  • doOnNext
  • doOnError
  • doOnCompleted
  • finallyDo
コメント
  • finallyDo以外はOnNext、OnError、OnCompletedらの処理をに追加されるので煩雑になるので?(ロジックが一か所ではない)

The retry technique

  • 例えば複数サーバが並列的に稼働している場合、ネットワークの問題などのエラーで全ての処理が行えなくなるより、別のサーバに再実行するなど重要な技術である

retry

  • エラーが発生したらObservableインスタンスの処理のチェインを最初から行う
  • retryメソッドに引数がない場合、永遠に再実行する
  • retryメソッドの引数に数値を渡した場合、その回数だけ再実行する

retryWhen

retry(Func2)

  • 第1引数が再実行回数
  • 第2引数がエラー情報
  • 戻り値に再実行ならtrue、OnErrorで終了ならfalseを返す

An Http Client Example

下記のJarが必要

  • gson
  • http-async-client
  • rx-apache-http
  • commons-logging
  • http-client
  • http-core
コメント
  • 意図した挙動にならない際のデバッグが難しい

2015-12-28 『ふつうのHaskellプログラミング』 もくもく読書会(第6章)の振り返り

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

  • 作者: 青木峰郎,山下伸夫
  • 出版社/メーカー: SBクリエイティブ
  • 発売日: 2014/10/05
  • メディア: オンデマンド (ペーパーバック)
  • この商品を含むブログを見る

第6章 基本的な値

Bool型

  • True
  • False

関数

  • not
  • &&
  • ||

数値

  • 数値リテラルだけだと具体的な型が定まらない
    → 数値の型のどれにでも推測できるから
  • 数値リテラルの型を限定するには明示的に宣言する
  • 一度推論や定義された型から別の型への自動変換は行わない
::

例:

defaultWidth  (16 :: Int)
  • マイナス値はかっこ()をつけないとエラー
(-1)

整数型

  • Int
  • Integer
  • 10進表記、8進表記、16進表記の3つのリテラルで表現できる

Int

  • 比較的小さな範囲の符号付整数値
  • 範囲は環境によって異なる(32ビットのGHCなら32ビットの符号付整数値)

Integer

  • 表現範囲に制限のない整数値

浮動小数点数

  • Float
  • Double

Float

単精度(32ビット)の浮動小数点数

Double

倍精度(64ビット)の浮動小数点数

Rational

  • 有理数(二つの整数でa/b という分数で表せる数)を表す
  • import Data.Ratioでインポートする ※ 本ではRatioモジュールをインポートとあるが今はData.Ratioに移動したもよう
  • Rational型を表す場合はカッコをつけないエラーになる
Data.Ratio> toRational 0.5
1 % 2
Data.Ratio> fromRational (1 % 2)
0.5
Data.Ratio> fromRational 1 % 2
<interactive>:56:1:
    No instance for (Fractional a0) arising from a use of ‘it’
    The type variable ‘a0’ is ambiguous
    Note: there are several potential instances:
      instance Integral a => Fractional (Ratio a)
        -- Defined in ‘GHC.Real’
      instance Fractional Double -- Defined in ‘GHC.Float’
      instance Fractional Float -- Defined in ‘GHC.Float’
    In the first argument of ‘print’, namely ‘it’
    In a stmt of an interactive GHCi command: print it

Complex

  • 複素数を表す
  • import Data.Complexでインポートする ※ 本ではComplexモジュールをインポートとあるが今はData.Complexに移動したもよう
コメント

複素数とはWikipedia によると

複素数(ふくそすう、complex number)は、実数 a, b と虚数単位 i を用いて a + bi と表せる数のことである。

とあるが、よくわからないのと使うことはないと思うので放置。(2乗するとマイナスになる値???)
→ 2乗するとマイナスになるのはiとのこと
→ 結論:『数学ガール』を読もう!

数学ガール (数学ガールシリーズ 1)

数学ガール (数学ガールシリーズ 1)

※ 色々でてるので、どれから読み始めればよいのかわからないが、とりあえず最初からか…

数値の演算

  • 演算する際に型が同じでなければならない
  • 自動で型変換は行われない
  • ただし文字リテラルのみの演算だと型推論が行われるので計算できる
> (1.0 :: Double) + (0.3 :: Float)

<interactive>:2:20:
    Couldn't match expected type ‘Double’ with actual type ‘Float’
    In the second argument of ‘(+)’, namely ‘(0.3 :: Float)’
    In the expression: (1.0 :: Double) + (0.3 :: Float)
    In an equation for ‘it’: it = (1.0 :: Double) + (0.3 :: Float)
> 1 + 0.3
1.3

加算(足し算)

備考
x + y

減算(引き算)

備考
x - y
abtract y x x - yとなる

乗算(掛け算)

備考
x * y

除算(割り算)

備考
x / y 結果は浮動小数点数となる
x div y 結果は割り算の整数部分。値は負の無限大向かってに丸められる。
x quot y 結果は割り算の整数部分。値は0に向かって丸められる。
x mod y x div y のあまり
x rem y x quot y のあまり
> 1/3
0.3333333333333333
> 12`div` 5
2
> (-12) `div` 5
-3
> 12`quot` 5
2
> (-12) `quot` 5
-2

その他の演算

備考
x ^ y x の y乗
abs x x の絶対値
odd x x が奇数ならTrue
even x x が偶数ならTrue

数値から別の数値への型変換

> :t toInteger
toInteger :: Integral a => a -> Integer
> :t toInteger
toInteger :: Integral a => a -> Integer
> :t fromIntegral
fromIntegral :: (Integral a, Num b) => a -> b

丸めの関数

関数 備考
ceiling x 切り上げ。マイナス値の場合は0に近い方になる。
floor x 切り捨て。マイナス値の場合は0に遠い方になる。
truncate x 切り捨て。マイナス値の場合は0に近い方になる。 ※ 本だと「trancate」になっているので注意
round x 四捨五入。マイナス値の場合は0に遠い方になる。
> :t ceiling
ceiling :: (Integral b, RealFrac a) => a -> b
> ceiling 1.3
2
> ceiling (-1.3)
-1
> :t floor
floor :: (Integral b, RealFrac a) => a -> b
> floor 1.5
1
> floor (-1.5)
-2
> :t truncate
truncate :: (Integral b, RealFrac a) => a -> b
> truncate 1.5
1
> truncate (-1.5)
-1
> :t round
round :: (Integral b, RealFrac a) => a -> b
> round 1.5
2
> round 1.4
1
> round (-1.5)
-2
> round (-1.4)
-1

文字と文字列

  • 文字はChar型
  • 文字コードUnicodeが使われる
  • Charを数値として扱うことはできない
  • 文字列は[Char]
  • String は[Char]の別名

タプル

  • 複数の型を持てるデータの塊
  • タプルは要素をカンマ区切りにし「()」でくくる
  • 1要素でなければ0要素でも複数要素を持つのも可能
  • 0要素のタプルはユニットと呼ばれる
  • 2つの要素はペアと呼ばれる

例:

(3, "aaaa") :: (Int, String)

zip関数

  • 2つのリストを1つのペア(タプル)のリストにする
  • リストの長さが一致しない場合は短いほうのリストの長さになる
> :t zip
zip :: [a] -> [b] -> [(a, b)]
> zip [1,2,3,4,5] ['a', 'b', 'c']
[(1,'a'),(2,'b'),(3,'c')]

unzip関数

  • ペアのリストを第1要素のリスト第2要素のリストの二つを持つリストを生成する
> :t unzip
unzip :: [(a, b)] -> ([a], [b])
> unzip [(1,'a'),(2,'b'),(3,'c')]
([1,2,3],"abc")

※ "abc"は[Char]

リスト

  • 同じ型の要素を格納するもの
  • 一方向のみで前から後ろにたどれるが逆は不可
  • リストは要素をカンマ区切りにし「[]」でくくる

:演算子

  • リストを生成する演算子
  • 要素 : リスト
  • 第1引数に要素、第2引数にリストをいれる
  • リストの最後は空のリスト
> :t (:)
(:) :: a -> [a] -> [a]
> 1 : 2 : 3 : []
[1,2,3]

リストの数列表記

  • ..を使って連番などを表現できる
  • 無限リストの場合は最後の要素を指定しない。例:[1..]
> [1..7]
[1,2,3,4,5,6,7]
> ['a'..'e']
"abcde"
> [1,3..11]
[1,3,5,7,9,11]

null関数

  • 空リストならTrue
> :t null
null :: Foldable t => t a -> Bool
> null [1,2,3]
False
> null []
True

リスト内包表記

  • map関数やfilter関数をあらわせる
  • 複数のリストとその要素もあつかえる
  • 汎用すると読みづらくなるので複雑なリスト内包表記は書かないほうが良い

map関数を表す場合

[ 要素の変数を使った式や関数 | 要素の変数 <- リスト ]

例:

> [x*2 | x <- [1,2,3]]
[2,4,6]

filter関数を表す場合

[ 要素の変数を使った式や関数 | 要素の変数 <- リスト, Boolの式 ]

例:

> [x*2 | x <- [1,2,3,4,5], (x `mod` 2) == 1]
[2,6,10]

複数のリストを扱う場合

> [(x,y)|x <- [1,2,3], y <-['a', 'b']]
[(1,'a'),(1,'b'),(2,'a'),(2,'b'),(3,'a'),(3,'b')]

catn.h

  • ファイルを読み込み内容を行番号をつけて標準出力するサンプル

zipLineNumberの定義

  • 引数に渡したリストから1から始まる数値とリストの要素のタプルにしたリストを生成
zipLineNumber :: [String] -> [(Int, String)]
zipLineNumber xs = zip [1..] xs

formatの定義

  • 行数と行を持つタプルを受け取り行数と行を合わせて1行にする
  • 行数は6文字の数日と前空白の文字列にする
  • 行数の文字列と行の間に空白をいれる
format :: (Int, String) -> String
format (n, line) = rjust 6 (show n) ++ " " ++ line
  • show nは数値を文字列にする
  • rjust 数値 文字列は" 文字列"という感じで引数の数値の文字数分の文字列にする関数

show関数

show :: (Show a) => a -> String
  • Show型の値を文字列変換する

練習問題

  • 答えに関しては特に問題はない
コメント
  • 「基本的な値」の章で関数が操作の論理的な結合順序の問題が出てくる意図がわからない。
    → 第8章で関数を扱ってるので、そこで出題するのならわかるが?

2015-12-28 『ふつうのHaskellプログラミング』 もくもく読書会(第5章)の振り返り

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

  • 作者: 青木峰郎,山下伸夫
  • 出版社/メーカー: SBクリエイティブ
  • 発売日: 2014/10/05
  • メディア: オンデマンド (ペーパーバック)
  • この商品を含むブログを見る

第5章 遅延評価

評価

  • プログラミングの世界では「実行」、「計算」とほぼ同じ意味
  • ラテン語の「ex + value」より来ている
    → 「値をひねり出す」の意味
  • Haskellの評価モデルは置き換えモデル(substitution model)で表現できる
  • Haskellの評価は遅延評価(lazy evaluation)である
  • 必要な式しか評価されない

最外簡約(outermost reduction)

Haskellの評価過程を段階的に表現する

square(1 + 3)
↓
(1 + 3) * (1 + 3)
↓
4 * 4
↓
16

この評価の仕方を最外簡約(outermost reduction)と言う

逆にJavaなどのように

square(1 + 3)
↓
square(4)
↓
4 * 4
↓
16

と評価されるのを最内簡約(innermost reduction)と言う

グラフ簡約(graph reduction)

  • square関数の引数の(1 + 3)は1度だけ評価され、それ以降は結果が共有される
  • この式を共有しながら簡約する方法をグラフ簡約(graph reduction)と言う

if.hs

Windowsコマンドプロンプトghcでif.hsをコンパイル後に下記で実行しようとしたらエラーになる

> if
コマンドの構文が誤っています。

if.exeもしくは本のように.\ifとする必要がある

データ構造の遅延評価

  • Haskellでは実際にプログラムで使われる要素だけ作成される

例1:

take 10 lines <= getContents

では、何万行もあるファイルを指定したとしても10行分しか文字列処理をしない

例2:

length[(1+1), (2+2), (3+3)]

では、リスト自体は全て評価されるが、各要素の式は評価されない

遅延評価で式の値が必要とされるタイミング

Haskellでは以下の2か所

  • パターンマッチ
  • 組み込み演算
myIf :: Bool -> a -> a -> a
myIf True t e = t
myIf False t e = e

では、myIf関数を使う際に第1引数は必ず評価されるが第2引数と第3引数は必要になるまで評価されない

参照透明性

  • 同じ引数を渡せば毎回同じ結果が返る性質
  • Haskellは参照透明である

遅延評価の利点

無限リストの使い道

  • 数学の問題でも解く状況下でない限り、無限リストは使いどころがないと思われがち
  • 標準入力は論理的に無限リスト
  • WEBアプリケーションでHTTPリクエストの無限リストを受け取ってHTTPレスポンスの無限リストを返す関数を考えるときれいにモデル化できる

インターフェイスを統一できる

  • 様々な処理をリスト処理にできる
  • 例えば巨大なツリー構造の全要素をリストにする場合
  • getContentsアクションもリスト処理で対応できる

コメント

遅延評価の欠点

  • 思った順番で操作を実行するのが難しい
  • デバッグしにくい

デバッグしにくい理由

  • 意味があるスタックトレースがとれない
  • どのような順序で評価されるのか予想がつかない

コメント

  • 意味があるスタックトレースがとれない理由があまりピンとこない
    → 参照透明性であるなら引数の値がわかれば毎回同じ順序で評価されるので追えそうな気もするが?
    → よく手で書きだすステップ実行的なイメージだったが、コンパイラが内部で色々やるらしい → それじゃ、無理だ…

2015-12-27 『JUnit実践入門』読書会(第4章)の振り返り

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

第4章 アサーション

  • Assert#assertThatメソッドとMatcher APIを使って値の比較検証を行う。
  • staticインポートを利用することを想定している

assertThatメソッド

  • 値の比較検証のほとんどを行う
  • 第1引数=実測値
  • 第2引数=期待値との比較を行うMatcherオブジェクト
  • 比較検証で値が一致しない場合、AssertionErrorが発生する
コメント
  • assertThatかJUnit3までのassertXXXのほうが好みかは人によって分かれるもよう
    → assertThatだと途中でisとか入れたくない
    → 旧メソッドの期待値を第1引数に入れるのが好きではない
    などなど

failメソッド

  • 無条件にテストを失敗させるメソッド
  • テストを実装していないことを明らかにしたい場合に使われる
  • あるブロックが実行されないことをテストしたい場合に使われる

Matcher API

  • 何かの値に対して等価かを柔軟に比較するためのライブラリ
  • ユニットテストのほとんどは等価かの比較
  • 要素を含むか(hasItems)などの比較方法も提供し、一貫した書式でテストの意図を伝えやすい
    assertTrue(actual.contains(expected))だとactualにexpectedを含むテストなのか、containsメソッドの意味なのかわかりにくい
    → hasItemメソッドは内部でcontains使ってる?
    → forループを使ってる

IsCollectionContaining.java

  protected boolean matchesSafely(
      Iterable<? super T> collection,
      Description mismatchDescription) {
    boolean isPastFirst = false;
    for (Object item : collection) {
      if (this.elementMatcher.matches(item)) {
        return true;
      }
      if (isPastFirst) {
        mismatchDescription.appendText(", ");
      }
      this.elementMatcher.describeMismatch(item,
          mismatchDescription);
      isPastFirst = true;
    }
    return false;
コメント
  • assertEqualsメソッドだと、どのフィールドが一致しないかの詳細な情報が伝わらない?
    → カスタムのMatcher APIを使わなくてもassertEqualsメソッドだけでも実装次第でできるのでメリットを感じられない?
    → 単にassertEqualsメソッドでもフィールド単位で較すればいいだけでは?
  • オブジェクトにequalsメソッドをきちんと実装してテストを行うのは問題?
    → あとでフィールドが追加された際にテスト実装するときにオブジェクトのequalsメソッド修正時に実装漏れに気づく?
    → フィールドが追加された場合に、実装がプロダクションでもテストでも抜けてしまっった場合、どっちにしろテストは失敗しないので気づかれないのでは?

CoreMathers

is

nullValue

assertThat(astual, is(nullValue()));

not

assertThat(actual, is(not(0)));

notNullValue

assertThat(actual, is(notNullValue()));

これは下記と同じ

assertThat(actual, is(not(nullValue())));

sameInstance

  • 値の比較ではなく同じインスタンスかの比較
  • equalsではなく==の比較
  • 基本データ型はボクシング変換されるので使うべきではない

instanceOf

  • 実測値(actual)が比較するクラスと同じクラスもしくは比較クラスを継承したクラスならtrue
  • 実測値(actual)がnulの場合は失敗になる

JUnitMathers

hasItem

  • 実測値に期待値が含まれているか判定する
  • 実測値は配列やIterableのオブジェクト

hasItems

  • 実測値に複数の期待値が全て含まれているか判定する
  • 複数の期待値を設定することが可能
  • 期待値の全てが含まれないとtrueにはならない

カスタムMatcherの作成

日付の比較を行うカスタムMathcerの作成

次の要件を満たすカスタムMatcherを作る

  • 年月日をそれおぞれintで指定して比較できる
  • 時分秒は無視する
  • 失敗したらyyyy/mm/dd表記で確認できるようにする

手順

  1. 独自Matcherクラスの生成

  2. org.hamcrest.baseMathcerを継承する

  3. matchesメソッドとdescribeToメソッドを実装する
  4. matchesメソッドは比較検証を行う
  5. describeToメソッドは失敗時の情報を作成する

  6. ファクトリメソッドを作成する

  7. Matcherを使いやすくするためstaticなファクトリメソッドを用意する

  8. 期待値を受け取るコンストラクタを用意し、ファクトリメソッドから生成する

  9. Matchesメソッドを実装する

  10. boolean値で結果を返すmatchesメソッドを実装する

  11. describeToメソッドを実装する

  12. matchesメソッドがfalseの際に呼び出される

  13. 引数のDescriptionインスタンスに情報を追加する
  14. appendText:文字列を追加するメソッド
  15. appendValue:値を追加するメソッド。出力時に「"」で囲まれる
  16. 本のフォーマットと違い4.12だと実測値がbut: wasになっている
  17. Descriptionに追加した情報はExpected isに表示される

テスト失敗時のメッセージ

Expected is "2000/01/01" but actual is "2015/12/27"
  but was <Sun Dec …略>
  • 期待値にエラー情報含めて出すのに抵抗ある
    メソッドが実績値、期待値含めたメッセージがつくれない点が微妙
    → 期待値にエラー情報含めて出すのはメッセージとして破たんしている
  • そもそも標準で用意されているメッセージ自体も実績値と期待値のインデントをそろえて出力したい(長文の文字列比較などやりやすいように)

2015-12-26 『Learning Reactive Programming with Java 8』 もくもく読書会(Chapter 4)の振り返り

Learning Reactive Programming With Java 8

Learning Reactive Programming With Java 8

Chapter 4: Transforming, Filtering, and Accumulating Your Data

Introduction

この章で見ていくこと

  • マーブルダイアグラムの紹介
  • マッピングを使ったデータ整形
  • データのフィルタリング
  • scanオペレータを使った値の集計

Observable transformations

Transformations with the various flatMap operators

flatMap

  • flatMapオペレータは受け取った値をfunctionを通してObservableインスタンスに変換する
  • functionを通って結果として生成されたいくつものObservaleインスタンスは一つのObservableインスタンスにマージされる
  • 結果のObservableインスタンスが持つ値は順序通りなっていない可能性があることに注意する

サンプルコード

本ではresourcesフォルダのlorem.txtとletters.txtの2ファイルを読み込み内容を出力するサンプルコードが提示されている。

listFolderメソッド

  • 対象の2ファイルのDirectoryStreamから生成したObservableインスタンスを生成する
  • Subsrcriber#add(Subscription)でSubscriberがunsubcribeされたらSubscritionを実行するようにしている
  • ここではfinlly句のようにSubscriptionをStream#closeを呼ぶために使っている

fromメソッド

  • Pathからファイルを読み込むObservableインスタンスを生成する
  • onNextメソッドに用見込んだ行を渡している
  • ここも同様にSubscriptionはReader#closeを呼び出すfinally句の用に使われている

実行パート

    Observable<String> fsObs = listFolder(
        Paths.get("src", "resources"),
        "{lorem.txt,letters.txt}")
            .flatMap(path -> from(path));
    Helpers.subscribePrint(fsObs, "FS");
  1. listFolderのPathを要素に持つObservableインスタンスが生成される
  2. flatMapで要素のPathをfromメソッドに渡している
  3. Helpers#subscribePrintメソッドで受けとったObservableを起動し、onNextの値を標準出力している

※ Chpter 6で説明するようだが、Schedularを使ったObservableインスタンスの場合、出力結果が順不同になる

flatMapのオーバーロードメソッド

  • flatMap(Action1 onNext, Action1 onError, Action0 onCompleted)
  • flatMap(Func1<T, Observable<U>>, Func2<T, U, R>)

flatMap(Func1<T, Observable<U>>, Func2<T, U, R>)

    Observable<Integer> flatMapped = Observable.just(5, 432)
        .flatMap(v -> Observable.range(v + 1, 3),
            (x, y) -> {
              System.out.println("x=" + x + ", y=" + y);
              return x + y;
            });
    Helpers.subscribePrint(flatMapped, "flatMap");

※ 本のサンプルを若干修正

実行結果

x=5, y=6
flatMap:11
x=5, y=7
flatMap:12
x=5, y=8
flatMap:13
x=432, y=433
flatMap:865
x=432, y=434
flatMap:866
x=432, y=435
flatMap:867
flatMap ended!
  1. 5と432の要素を持つObservableインスタンスを生成
  2. 5の要素が渡されたときrangeメソッドで{6, 7, 8}の要素を持つObservableインスタンスを生成
  3. 2のObservableインスタンスの要素がx+yを実行される関数に渡されxにjustメソッドの値、yにranngeの値が渡される

  4. このflatMapのオーバーロードはソースとなった要素にアクセスしないといけない場合に使いやすい(タプルなどに値を格納してそれを通じて値を渡さなくてよい)

flatMapIterable

    Observable<?> flatMapped = Observable
        .just(Arrays.asList(2, 4),
            Arrays.asList("two", "four"))
        .flatMapIterable(v -> {
          System.out.println("v=" + v);
          return v;
        });
    Helpers.subscribePrint(flatMapped, "flatMapIterable");

※ 本のサンプルを若干修正

実行結果

v=[2, 4]
flatMapIterable:2
flatMapIterable:4
v=[two, four]
flatMapIterable:two
flatMapIterable:four
flatMapIterable ended!
  • flatMap(v -> Observable.from(v))と同じ

concatMap

A significant different between the flatMap and concatMap operator is that the flatMap operator uses the inner Observable instances in parallel, whereas the concatMap operator only subscribes to one of the Observable instances at a time.

  • flatMapとの違いはflatMapが内部のObservableインスタンスを並列に使うのに対し、concatMapはObservableインスタンスを一つずつ使う
    → とあるが、flatMapも一つずつやってるようにしか見えないのだが?
    Observable<Integer> flatMapped = Observable
        .from(Arrays.asList(5, 6, 7)).flatMap(v -> {
          try {
            Thread.sleep(1000L);
            return Observable.just(v * 2);
          } catch (InterruptedException e) {
            e.printStackTrace();
            return Observable.empty();
          }
        });
    Helpers.subscribePrintWithThreadName(flatMapped,
        "flatMap");

    Observable<Integer> concatMapped = Observable
        .from(Arrays.asList(5, 6, 7)).concatMap(v -> {
          try {
            Thread.sleep(1000L);
            return Observable.just(v * 2);
          } catch (InterruptedException e) {
            e.printStackTrace();
            return Observable.empty();
          }
        });
    Helpers.subscribePrintWithThreadName(concatMapped,
        "concatMap");

実行結果

main:flatMap:10
main:flatMap:12
main:flatMap:14
flatMap ended!
main:concatMap:10
main:concatMap:12
main:concatMap:14
concatMap ended!

と思っていたのだが、intervalから生成したObservableインスタンスの場合、結果が違っていた。

    Observable<Object> concatMapped = Observable
        .interval(1000L, TimeUnit.MILLISECONDS)
        .concatMap(v -> Observable
            .interval(300L, TimeUnit.MILLISECONDS)
            .map(u -> "Observable <" + (v) + "> : "
                + (v + u)));
    Subscription subscription = Helpers
        .subscribePrint(concatMapped, "concatMap");

    Thread.sleep(5000L);
    subscription.unsubscribe();

    Observable<Object> flatMapped = Observable
        .interval(1000L, TimeUnit.MILLISECONDS)
        .flatMap(v -> Observable
            .interval(300L, TimeUnit.MILLISECONDS)
            .map(u -> "Observable <" + (v) + "> : "
                + (v + u)));
    Helpers.subscribePrint(flatMapped, "flatMap");
    Thread.sleep(5000L);

実行結果

concatMap:Observable <0> : 0
concatMap:Observable <0> : 1
concatMap:Observable <0> : 2
concatMap:Observable <0> : 3
concatMap:Observable <0> : 4
concatMap:Observable <0> : 5
concatMap:Observable <0> : 6
concatMap:Observable <0> : 7
concatMap:Observable <0> : 8
concatMap:Observable <0> : 9
concatMap:Observable <0> : 10
concatMap:Observable <0> : 11
concatMap:Observable <0> : 12
flatMap:Observable <0> : 0
flatMap:Observable <0> : 1
flatMap:Observable <0> : 2
flatMap:Observable <0> : 3
flatMap:Observable <1> : 1
flatMap:Observable <0> : 4
flatMap:Observable <1> : 2
flatMap:Observable <0> : 5
flatMap:Observable <1> : 3
flatMap:Observable <0> : 6
flatMap:Observable <1> : 4
flatMap:Observable <2> : 2
flatMap:Observable <0> : 7
flatMap:Observable <1> : 5
flatMap:Observable <2> : 3
flatMap:Observable <0> : 8
flatMap:Observable <1> : 6
flatMap:Observable <2> : 4
flatMap:Observable <0> : 9
flatMap:Observable <1> : 7
flatMap:Observable <2> : 5
flatMap:Observable <3> : 3
flatMap:Observable <0> : 10
flatMap:Observable <1> : 8
flatMap:Observable <2> : 6
flatMap:Observable <3> : 4
flatMap:Observable <0> : 11
flatMap:Observable <1> : 9
flatMap:Observable <2> : 7
flatMap:Observable <3> : 5
flatMap:Observable <0> : 12
flatMap:Observable <1> : 10

このようにconcatMapの場合、最初の要素が終わらないと次の要素の処理を始めない。

switchMap

  • flatMapとの違いはswitchMapはソースのObservableインスタンスから新しい要素が発行されるとその前の要素の処理をやめてしまう
    Observable<Object> switchMapped = Observable
        .interval(1000L, TimeUnit.MILLISECONDS)
        .switchMap(v -> Observable
            .interval(300L, TimeUnit.MILLISECONDS)
            .map(u -> "Observable <" + (v) + "> : "
                + (v + u)));
    Subscription subscription = Helpers
        .subscribePrint(switchMapped, "switchMap");

    Thread.sleep(3000L);
    subscription.unsubscribe();

    Observable<Object> flatMapped = Observable
        .interval(1000L, TimeUnit.MILLISECONDS)
        .flatMap(v -> Observable
            .interval(300L, TimeUnit.MILLISECONDS)
            .map(u -> "Observable <" + (v) + "> : "
                + (v + u)));
    Helpers.subscribePrint(flatMapped, "flatMap");
    Thread.sleep(3000L);

実行結果

switchMap:Observable <0> : 0
switchMap:Observable <0> : 1
switchMap:Observable <0> : 2
switchMap:Observable <1> : 1
switchMap:Observable <1> : 2
switchMap:Observable <1> : 3
flatMap:Observable <0> : 0
flatMap:Observable <0> : 1
flatMap:Observable <0> : 2
flatMap:Observable <0> : 3
flatMap:Observable <1> : 1
flatMap:Observable <0> : 4
flatMap:Observable <1> : 2
flatMap:Observable <0> : 5
flatMap:Observable <1> : 3

Grouping items

groupBy(Func1)

  • groupByオペレータはキーでグループ化されたGroupedObservableインスタンスを生成する
  • 引数のFunc1でキーを生成する
  • GroupedObservableインスタンスを生成するとそのインスタンスは要素をバッファするため、気を付けないとメモリーリークが発生する
  • onNextで発行される順番はソースと同じだがGroupedObservbleインスタンスはキー毎に異なる

The order the items are emitted in is the same, but they are emitted by different GroupedObsevable instances.

※ とあるが実行結果のonCompleted時のメッセージがキー毎なのでキー毎に違うインスタンスと言いたいのでは?

  • ソースが終了した時にGroupedObservableも終了になる
    List<String> albums = Arrays.asList(
        "The Piper at the Gates of Dawn",
        "A Sauceful of Secrets", "More", "Ummagumma",
        "Atom Heart Mother", "Meddle", "Obscured by Clouds",
        "The Dark Side of the Moon", "Wish You Were Here",
        "Animals", "The Wall");
    Observable.from(albums)
        .groupBy(album -> album.split(" ").length)
        .subscribe(obs -> Helpers.subscribePrint(obs,
            obs.getKey() + " word(s)"));

    Thread.sleep(2000L);

実行結果

7 word(s):The Piper at the Gates of Dawn
4 word(s):A Sauceful of Secrets
1 word(s):More
1 word(s):Ummagumma
3 word(s):Atom Heart Mother
1 word(s):Meddle
3 word(s):Obscured by Clouds
6 word(s):The Dark Side of the Moon
4 word(s):Wish You Were Here
1 word(s):Animals
2 word(s):The Wall
1 word(s) ended!
2 word(s) ended!
3 word(s) ended!
4 word(s) ended!
6 word(s) ended!
7 word(s) ended!

groupBy(Func1, Func2)

  • 第1引数でキーを作成
  • 第2引数で値を生成

Additional useful transformation operators

cast

  • 要素をキャストする
    List<Number> list = Arrays.asList(1, 2, 3);
    Observable<Integer> observable = Observable.from(list)
        .cast(Integer.class);

timetamp

    Observable<Timestamped<Integer>> observable = Observable
        .from(Arrays.asList(1, 2, 3)).timestamp();
    Helpers.subscribePrint(observable, "timestamp");

    observable.subscribe(item -> {
      long timestamp = item.getTimestampMillis();
      int value = item.getValue();

      System.out.println("timestamp = " + timestamp
          + ", value = " + value);
    });

timeinterval

Filtering data

filter

  • ソースから対象がtrueになる要素しか持たないObserverableインスタンスを生成する

その他のfilter系のメソッド

    Observable<Integer> observable = Observable.range(1, 5);

    // 複数要素を持つ可能性があるもの
    Helpers.subscribePrint(observable.takeLast(3),
        "takeLast");
    Helpers.subscribePrint(observable.take(3), "take");
    Helpers.subscribePrint(observable.skipLast(3),
        "skipLast");
    Helpers.subscribePrint(observable.skip(3), "skip");

    Helpers.subscribePrint(observable.takeLastBuffer(3),
        "takeLastBuffer"); // 要素はList<Integer>

    // 唯一の要素を取得するもの
    Helpers.subscribePrint(observable.last(), "last");
    Helpers.subscribePrint(observable.first(), "first");

    Helpers.subscribePrint(
        observable.takeFirst(value -> value % 2 == 0),
        "takeFirst");

    Helpers.subscribePrint(observable.elementAt(3),
        "elementAt");

    // デフォルト値ありのもの
    Helpers.subscribePrint(observable.lastOrDefault(99),
        "lastOrDefault:値あり");
    Helpers.subscribePrint(
        Observable.empty().lastOrDefault(99),
        "lastOrDefault:値なし");

    Helpers.subscribePrint(observable.firstOrDefault(99),
        "firstOrDefault:値あり");
    Helpers.subscribePrint(
        Observable.empty().firstOrDefault(99),
        "firstOrDefault:値なし");

    Helpers.subscribePrint(
        observable.elementAtOrDefault(3, 99),
        "elementAtOrDefault:値あり");
    Helpers.subscribePrint(
        observable.elementAtOrDefault(10, 99),
        "elementAtOrDefault:値なし");

    // 重複を除く
    Helpers.subscribePrint(
        Observable.from(Arrays.asList(1, 2, 3, 4, 1, 2, 3))
            .distinct(),
        "distinct");

    // 型のフィルター
    Helpers.subscribePrint(Observable
        .from(Arrays.asList("1", 2, 3L, 4.0, 1, "2", 3.0))
        .ofType(Integer.class), "ofType");

実行結果

takeLast:3
takeLast:4
takeLast:5
takeLast ended!
take:1
take:2
take:3
take ended!
skipLast:1
skipLast:2
skipLast ended!
skip:4
skip:5
skip ended!
takeLastBuffer:[3, 4, 5]
takeLastBuffer ended!
last:5
last ended!
first:1
first ended!
takeFirst:2
takeFirst ended!
elementAt:4
elementAt ended!
lastOrDefault:値あり:5
lastOrDefault:値あり ended!
lastOrDefault:値なし:99
lastOrDefault:値なし ended!
firstOrDefault:値あり:1
firstOrDefault:値あり ended!
firstOrDefault:値なし:99
firstOrDefault:値なし ended!
elementAtOrDefault:値あり:4
elementAtOrDefault:値あり ended!
elementAtOrDefault:値なし:99
elementAtOrDefault:値なし ended!
distinct:1
distinct:2
distinct:3
distinct:4
distinct ended!
ofType:2
ofType:1
ofType ended!

Accumulating data

scan

  • scanメソッドは2引数を取る関数を設定し要素を使った集計処理を行う。
  • Observableインスタンスは各要素ごとの結果を持ち、最後のものが最終的な集計結果
  • 関数の第1引数は以前までの結果(最初は第1要素)
  • 関数の第2引数はソースの要素
  • このような関数はaccumulatorと言われる
  • reduce(Func2)scan(Func2).lastと同じ

下記は1~10までの足し算をするサンプル

    Observable<Integer> observable = Observable.range(1, 10)
        .scan((p, v) -> {
          System.out.println("p=" + p + ", v=" + v);
          return p + v;
        });
    Helpers.subscribePrint(observable, "scan");

実行結果

scan:1
p=1, v=2
scan:3
p=3, v=3
scan:6
p=6, v=4
scan:10
p=10, v=5
scan:15
p=15, v=6
scan:21
p=21, v=7
scan:28
p=28, v=8
scan:36
p=36, v=9
scan:45
p=45, v=10
scan:55
scan ended!

コメント

  • マーブルダイアグラムは全部がわかりにくいわけではないが細かいことがわかりにくい(flatMapとconcatMapの違いとか)