2016年2月28日日曜日

C++プログラマー向けRust 翻訳シリーズ2

制御フロー

If


if文は、C++のものと全く一緒だ。一つ違いがあるとしたら、大かっこは必須であり、条件式を囲むかっこはそうではないことだ。
また、Rustのはif式であるため、C++の三項演算子と同じように使用することができる(前回、ブロックの最後の式がセミコロンで閉じていなければ、その値がブロックの戻り値になることを思い出してほしい)。
Rustには三項演算子は存在しない。ゆえに、下記の関数は双方とも同じ挙動をする。

fn foo(x: i32) -> &'static str {
    let mut result: &'static str;
    if x < 10 {
        result = "less than 10";
    } else {
        result = "10 or more";
    }
    return result;
}

fn bar(x: i32) -> &'static str {
    if x < 10 {
        "less than 10"
    } else {
        "10 or more"
    }
}
一番目のコードは、C++のコードの直訳形だ。二番目がRustでは好ましい。

let x = if ...などとすることもできる。

ループ


RustにもまたC++のようにwhileループがある。

fn main() {
    let mut x = 10;
    while x > 0 {
        println!("Current value: {}", x);
        x -= 1;
    }
}
Rustには、do...whileループはないが、単に永久ループするloop文がある。

fn main() {
    loop {
        println!("Just looping");
    }
}
RustにもC++同様、breakcontinueが存在する。

Forループ


Rustにもforループが存在する。しかし、こちらは少し勝手が違う。
整数型のvectorコンテナがあって、すべて表示したいとしよう(vectorコンテナ、配列、イテレータ、およびgenericsについては後ほど詳述する。とりあえず、Vec<T>T型のシーケンスであり、iter関数は順繰りに処理できるものからイテレータを返すものと思っておけばいい)。
シンプルなforループは、以下のような見た目をしている。

fn print_all(all: Vec<i32>) {
    for a in all.iter() {
        println!("{}", a);
    }
}

all変数のインデックスをたどりたい場合(よりC++の配列に作用するforループ風に)、以下のようにも書ける。
fn print_all(all: Vec<i32>) {
    for i in ..all.len() {
        println!("{}: {}", i, all.get(i));
    }
}
len関数が何をするかは容易に想像が付くだろう。

Switch/Match


Rustには、match式がある。これはC++のswitch文に似ているが、ずっと強力だ。
以下のシンプルバージョンは、馴染み深く感じられるだろう。

fn print_some(x: i32) {
    match x {
        0 => println!("x is zero"),
        1 => println!("x is one"),
        10 => println!("x is ten"),
        y => println!("x is something else {}", y),
    }
}
見た目に違いがある。評価された値と実行する式の対応付けに=>という記号を使っているし、match節の区切りに,を使用している(最後の,はあってもなくてもいい)。
さらに意味にもあまり明瞭でない違いがある。評価値は網羅性がなければならない。つまり、評価対象の式(上記の例では変数x)がマッチする可能性のある値すべてが記述されていなければならない。
y => ...の行を削除してどうなるか見てみるといい。そうなるのは、0と1と10にマッチする値しか指定されておらず、未指定の値が数多くあるからだ。最後の節で変数yが評価値(今回のケースでは変数x)に紐づけられている。
次のようにも記述できる。

fn print_some(x: i32) {
    match x {
        x => println!("x is something else {}", x)
    }
}
ここで、match節内の変数xは、引数のxを隠蔽している。これは、内側のスコープで変数を宣言するのと同じだ。

変数に名前をつける必要がなければ、無名変数に_とつけることができる。これは、ワイルドカードマッチを行うのと同じだ。
何もする必要がなければ、空の節を用意すればいい。

fn print_some(x: i32) {
    match x {
        0 => println!("x is zero"),
        1 => println!("x is one"),
        10 => println!("x is ten"),
        _ => {}
    }
}
他にも次のmatch節へのフォールスルーがないという意味の違いもある。

matchは非常に強力だと後ほどの投稿で認識できるだろう。今のところは、もう二つばかり、その機能を紹介しておきたい。or演算子と限定if節だ。
次の例で自明だと嬉しい。

fn print_some_more(x: i32) {
    match x {
        0 | 1 | 10 => println!("x is one of zero, one, or ten"),
        y if y < 20 => println!("x is less than 20, but not zero, one, or ten"),
        y if y == 200 => println!("x is 200 (but this is not very stylish)"),
        _ => {}
    }
}
if式同様、match文も実は式なので、先ほどの例は以下のように書き換えられる。

fn print_some_more(x: i32) {
    let msg = match x {
        0 | 1 | 10 => "x is one of zero, one, or ten",
        y if y < 20 => "x is less than 20, but not zero, one, or ten",
        y if y == 200 => "x is 200 (but this is not very stylish)",
        _ => "something else"
    };

    println!("x is {}", msg);
}
閉じかっこの後のセミコロンに注目してほしい。これは、let文が文であり、let msg = ...;という形にしなければいけないからだ。
右辺にmatch式を代入しているが(match式は通常セミコロンで閉じる必要はない)、let文には必要なのだ。いつも引っかかるんだよな〜。

理由付け:Rustのmatch文では、C++のswitch文でありがちなバグ(breakキーワードを忘れたり、意図なしにフォールスルーすることがない)を回避することができる。また、enum(詳しくは後ほど)に値を追加したら、コンパイラがmatch文に含まれているか確認してくれる。

メソッド呼び出し


最後に、ちょこっとだけRustにもC++のようにメソッドが存在することに注目しよう。Rustのメソッドは必ず.演算子で呼び出す(->演算子はない。これについてはまた別立てで解説する)。すでに数個(len,iter)例が挙がっていた。
定義の仕方や呼び出し方についての詳述は、また将来することにしよう。C++やJava流の推測は概ね当たっているよ。


原文: https://github.com/nrc/r4cppp/blob/master/control%20flow.md

2016年2月27日土曜日

C++プログラマー向けRust 翻訳シリーズ1

導入 - hello world!


もし今現在、あなたがCやC++を使用しているとしたら、その理由はおそらく他に選択肢がないからだろう。(低レベルレイヤーへのアクセスが必要だとか、パフォーマンスを極限まで追求しなければならないとか、あるいはその両方かも)
RustはCやC++と同レベルのメモリ抽象性(翻訳者注:生ポインタへのアクセスや不安全なキャストなどを指していると思われる)、パフォーマンスを発揮しつつも、より安全で生産性を向上させることを目的にしている。

厳密に言えば、世の中にはC++よりも使いやすいプログラミング言語が溢れている。Java, Scala, Haskell, Pythonなどなど。だが、抽象性が高すぎ(メモリアドレスが取れない、ガベージコレクションを使わざるを得ないなど)たり、パフォーマンス(パフォーマンスの予測が立たないか、単純に遅い)の問題で使えないのだろう。Rustではガベージコレクションを強制されることはないし、C++同様、生ポインタをいじることもできる。RustはC++の「自分が使用した分だけ払え」精神に倣っている。機能を使用しなければ、その機能の存在自体にパフォーマンスを犠牲にする必要はないのだ。さらに、Rustの言語仕様はすべて、予測可能なコスト(そして、大抵は極小)内にある。

これらの制約によりRustはC++の類稀で有望な代替言語となっているけれども、Rust自体にも利点はある。
一つ目はメモリ安全性だ。Rustの型システムにより、C++で一般に見られるようなメモリ関連のエラーは発生しない。
メモリリーク、未初期化領域アクセス、nullポインタ。すべてRustでは起こりえない。
その上、Rustでは他の制約の範囲内ならば、安全性問題を回避する手段も用意している。例えば、配列は境界値チェックが行われる。(無論、そのコストが惜しければ、(安全性を犠牲にして)回避することもできる。Rustではunsafeブロックを使って、その他様々な非安全な事柄とともに行うことができる。根底的に、Rustでは非安全なことはunsafeブロック内にとどまりプログラム全体に影響を及ぼすことはないのだ)
最後に、Rustは現代のプログラミング言語の様々なコンセプトを取り込み、システム言語の世界に導入している。願わくば、それによってRustが生産的かつ効率的で、遊び心のあるものになっておらんことを。

これから、Rustをダウンロード、インストールし、最低限のCargoプロジェクトの生成、そしてHello worldの実装を行う。


Rustの入手


Rustはhttp://www.rust-lang.org/install.htmlから入手できる。ダウンロードしたファイルにはコンパイラ、標準ライブラリ、そしてCargoと呼ばれるRustのパッケージマネージャ兼ビルドサポートツールが含まれている。

Rustは3段構えでリリースされており、それぞれ安定版、β版、ナイトリー版となっている。Rustは短期リリーススケジュールを採用しており、6週間ごとにリリースが更新される。
更新日にナイトリー版がβ版に、β版が安定版になるのだ。

ナイトリー版は毎晩更新されるため、最新の機能を試したり、長期的に動作するライブラリの製作者向けだ。

一方、安定版は大抵のユーザに最適である。Rustの安定性は安定版のみで保障されている。

β版は、ユーザのコードが想定通り動作していることをチェックイン(翻訳者注:CIをCheck inのabbreviationと解釈した。間違ってるかも)の段階で確認する目的で作られている。

以上より、安定版を入手しよう。LinuxやOS Xユーザならば、以下の方法で簡単に入手できる。
curl -sSf https://static.rust-lang.org/rustup.sh | sh
他のインストール方法は、http://www.rust-lang.org/install.htmlを参照されたし。

ソースコードはhttps://github.com/rust-lang/rustで入手できる。コンパイラをビルドするには./configure && make rustcコマンドを入力しよう。もっと詳しい手順については、building-from-sourceを参照されたし。


Hello World!


Rustのプログラムをビルドする最も簡単で一般的な方法は、Cargoを使用することだ。 helloという名のプロジェクトを作成するのに、cargo new --bin helloと入力しよう。これで、helloという名のディレクトリができ、Cargo.tomlというファイルとmain.rsというファイルが入ったsrcという名のディレクトリが入っているはずだ。
Cargo.tomlにプロジェクトに関するメタデータや依存するライブラリなどが定義されている。詳しくは後述する。
ソースコードはすべてsrcディレクトリに置く。main.rsにはすでにHello Worldプログラムが入っている。以下のような感じだ。
fn main() {
    println!("Hello world!");
}

このソースをビルドするにはcargo buildと入力する。ビルドして実行するには、cargo runと入力する。後者の方法をとったなら、コンソールに挨拶が表示されるはずだ。やったね!
Cargoがtargetディレクトリを生成し、その中に実行ファイルが配置される。
もし、コンパイラを直接起動する必要があるなら、rustc src/hello.rsコマンドを使用しよう。helloという名の実行ファイルが作成される。他のオプションについては、rustc --helpコマンドを参照しよう。

さて、コードに戻ろう。数点気になる箇所がある。関数やメソッドを定義するのにfnキーワードを使っているのだ。main()がプログラムの規定エントリーポイントだ(プログラムの引数については後ほど見ることにしよう)。
C++のように、個別の定義やヘッダーファイルは存在しない。println!はRust流のprintfだ。!記号がマクロであることを意味している。マクロについてはとりあえず、普通の関数のようなものと思っておけばいいだろう。
importやincludeすることなく、標準ライブラリの一部が使用できる(後ほど詳しく語る)。println!マクロはその一部分に含まれているのだ。

例を少し変えてみよう。

fn main() {
    let world = "world";
    println!("Hello {}!", world);
}
letで変数を宣言する。worldは変数名で型はstringだ(技術的には&'static strだが、また別立てで詳しく解説しよう)。型を明示する必要はない。型は推論されるのだ。

println!文で{}を使用するのは、printf関数内で%sを用いるようなものだ。実際、{}は%sよりも少々一般的で、もし変数の型がstringでなければ、コンパイラがstring型への変換を試みるのだ。この類の事柄は、容易に遊んでみることができる。いろんな文字列、数字で試してみよう(整数リテラルとfloatリテラルなら動作する)。

好みに応じて、world変数の型を明示することもできる。
let world: &'static str = "world";
C++ではT型の変数xを定義する際、T xと記述した。Rustでは、let文内だろうが、関数宣言だろうが、x: Tと記述する。ほとんどの場合、let文では型の明示は行う必要がないが、関数の引数では明示しなければならない。新たな関数を追加して実際の動作を確認してみよう。

fn foo(_x: &'static str) -> &'static str {
    "world"
}

fn main() {
    println!("Hello {}", foo("bar"));
}
関数fooは文字列リテラル引数_xを一つ持っている(main関数内で"bar"を渡している)。

関数の戻り値の型は->の後に記述する。関数の戻り値がない場合(C++のvoid関数)は、戻り値を明示する必要はない(main関数でもそうしていた)。あなたが冗長性を好む人間ならば、-> ()と書くこともできる。()はRust流のvoid型だ。

Rustでは、returnキーワードは必要ない。関数本体の最後の式(あるいは詳細は後述するが、他のどんな包含式)がセミコロンで閉じていなければ、戻り値になる。したがって、foo関数は常に"world"文字列を返す。なお、returnキーワードは存在しており、短絡評価のために用いることができる。"world"return "world";と書いても同じ意味になるのだ。

理由


前述の機能の動機付けを行おう。
ローカル変数の型推論は便利であり、パフォーマンスや安全性を犠牲にすることなく使用できる(現バージョンのC++にも入っている)。
より目立たない利便性として、言語仕様がキーワード(fnletなど)と紐づくことが挙げられる。そのため、ツールや目で識別するのが簡単になる。一般的に言って、Rustの見た目はC++のものよりも単純で一貫性がある。
println!マクロはprintf関数よりも安全だ。つまり、引数の数は静的に文字列内の「穴」の数と突合され、引数自体も型チェックが行われる。これは要するに、printf関数でありがちな、型違いのメモリ領域を表示したり、誤ってメモリスタックをより深くまで掘り下げてしまうなどのミスを犯さなくなるということだ。
これらはほんの些細なことだが、Rustの仕様の背景にある哲学を描き出していると嬉しい。


  1. こちらは、Display traitを使用してプログラマーが記法をカスタマイズしたバージョンだ。このDisplay traitはJavaのtoStringのような役割を果たす。{:?}という記法もでき、こちらはデバッグの際に利便性を発揮するコンパイラ生成の文字列を表示する。printf関数と同じように、その他にも様々なオプションがある。
  2. foo関数の引数は使用していない。普通なら、コンパイラがこのことについて警告を出してくれるが、変数名に_という接頭辞をつけることでこの警告を回避している上、変数名を付す必要はなく、ただ単に_という名前をつければいいのだ。



翻訳者後記:以前、C++とRustの概念比較の記事を書いてから1年以上が経ち、Rustのバージョンも1.5とだいぶこなれてきました。
暇があるときにC++習熟者向けに書かれたRust解説書のgit bookバージョンであるhttps://github.com/nrc/r4cpppを軽く訳していこうと思います。
のんびりお付き合いいただければと思います。
なお、本文は意訳だらけなので、原語版と比較して読むのはあまりお勧めしません。

原文: https://github.com/nrc/r4cppp/blob/master/hello%20world.md

2016年2月26日金曜日

CoreDataによる集計とインメモリ集計の比較

どうも、こんにちは。はざまです。今回は珍しく、前回の投稿からあまり日にちが空いてませんね。
さて、iOS向けお小遣い帳アプリを作っていてCoreData内に保存したデータの合計金額を算出しなければならない場面に遭遇したため、その方法についてメモしておきます。

素直に考えると、CoreData内に保存したデータの集計を行う方法には2通りあることがわかります。一つ目は、CoreDataがバックストアに使用しているSQLiteの集計関数を活用する方法。二つ目が、一旦集計を行うオブジェクトをすべてフェッチして、Swift側で集計を行う方法です。
単純に考えて、一つ目の方法の方が速度は速いでしょう。しかし、バックストアにSQLiteを使用していることから、金銭を扱うアプリでは必須とも言える10進数型での集計は行えなさそうです。今回は軽くベンチマークも取ってみたので、その辺のトレードオフも含めて、SQLite組み込みの浮動小数点型とiOS組み込みの10進数型での合計値算出方法を比較してみたいと思います。


func calculateSumUsingCoreData()
{
    let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
    let request = NSFetchRequest()
    request.entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: context)
        
    let expr_description = NSExpressionDescription()
    expr_description.name = "sum"
    expr_description.expression = NSExpression(forFunction: "sum:", arguments: [NSExpression(forKeyPath: "price")])
    expr_description.expressionResultType = .DecimalAttributeType  //ここで.DecimalAttributeTypeを指定していても、結果はdoubleかfloatでしか取れない
        
    request.propertiesToFetch = [expr_description]
    request.resultType = .DictionaryResultType
        
    do {
        let results = try context.executeFetchRequest(request)
        if results.count > 0 {
            let extracted_results = results.map {(elem: AnyObject) -> Double in
                let dict = elem as! [String: Double]
                let func_reuslt = dict["sum"]
                return func_reuslt!
            }
            return extracted_results[0]
        } else {
            return 0.0
        }
    } catch {
        let ns_error = error as NSError
        print("\(ns_error), \(ns_error.userInfo)")
        return Double.NaN
    }
}

func calculateSumWithReduce()
{
    let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
    let request = NSFetchRequest()
    request.entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: context)
        
    do {
        let entities = try context.executeFetchRequest(request)
        if entities.count > 0 {
            let events = entities as! [Event]
            let result = events.reduce(NSDecimalNumber(integer: 0)) { (currentTotal, event) in
                let price = event.price!
                return currentTotal.decimalNumberByAdding(price)
            }
            return result
        } else {
            return NSDecimalNumber(integer: 0)
        }
    } catch {
        let ns_error = error as NSError
        print("\(ns_error), \(ns_error.userInfo)")
        return NSDecimalNumber(integer: 0)
    }
}

合計値を算出するコードは、それぞれ以上の通りです。これを新規プロジェクトウィザードのMaster-Detail Applicationテンプレートに当て込んでベンチマークを行います。ちなみに計測を行う部分はこんな感じです。

let RepeatTimes = 10_000
func viewDidLoad()
{
    if let label = self.detailDescriptionLabel {
        let start = NSDate()
        for _ in 0..<RepeatTimes {
            _ = calculateSumUsingCoreData()
        }
        let end = NSDate()
                
        let elapsed_time = (end.timeIntervalSinceReferenceDate - start.timeIntervalSinceReferenceDate) * 1000.0
        let sum = calculateSumUsingCoreData()
        label.text = "pure core data: \(sum.description), time elapsed: \(elapsed_time)"
    }
            
    if let label2 = self.detailDescriptionLabel2 {
        let start2 = NSDate()
        for _ in 0..<RepeatTimes {
            _ = calculateSumWithReduce()
        }
        let end2 = NSDate()
                
        let elapsed_time2 = (end2.timeIntervalSinceReferenceDate - start2.timeIntervalSinceReferenceDate) * 1000.0
        let sum2 = calculateSumUsingFor()
        label2.text = "using reduce: \(sum2.stringValue), time elapsed: \(elapsed_time2)"
    }
}

集計対象のデータは、0〜100,000の整数乱数を生成して10で割り、0.0〜10,000.0までの浮動小数点数を100個としています。
ベンチマークを行う端末は、去年までメインで使用していたiPhone5Sです。
そして、その結果がこちら。

純粋CoreDataによる集計法の方が、約3.5倍速いという結果になりました。しかし、そのトレードオフとして、純粋CoreDataの方法では結果が浮動小数点数になることを忘れてはなりません。
さらに少し気になってデータ数を200個にした場合も試してみました。その結果は次の通り。
なんと今度は、CoreDataのみの方が約6.5倍も速いという結果に。(ちなみにまだApple Developer Programに登録していないため、どちらもDebugビルドでの結果になります。正式にアプリをApp Storeに公開したら、再計測してみたいと思います)
さらにさらに、データ数400個の場合。
今度は約11.2倍。結構リニアに差が開いていきます。Swiftで集計する方はメモリーの消費量もかなり大きく、シチュエーションを選びそうです。
う〜ん、よっぽどデータに偏りがない限り、CoreDataの集計関数を使えば誤差もなく十分な気がしなくもないような……

2016年2月9日火曜日

関数型プログラミングスタイルのすゝめ(後編)

前編だけ公開して、後編は眠らせたままにしていたようです。
後編の今回は、実際のコードを交えてより実践的な視点から関数型スタイルプログラミングを俯瞰してみようと思います。では、まずは以下のサンプルコードを開いてみてください。



上記のサンプルコードは、C#を使用して関数型スタイルと非関数型スタイルで0から100までの偶数を並べあげる処理を記述したものです。コメントを全て英語で記述したため、ソースコード単体だと読みにくいかもしれないですが、上から順に、forループ、foreachループ、LINQ to Objects、LINQ to SQLを使用しています。このうち、まず、本筋とあまり関係はないけど、補足が必要そうなところから解説します。
foreachループ以降のソリューションで登場する
Enumerable.Range(from, to, step=1)
メソッドは、fromからtoまでstep刻みの整数列をIEnumerableオブジェクトとして生成します。

さて、本題の関数型スタイルに入りましょう。関数型言語と聞くと、Haskellを筆頭として、C系の手続き型スタイルに慣れてしまったプログラマーにとってみると、どうしてもとっつきにくい印象を抱いてしまう人が多いことでしょう。確かに、文(値を返さない文法構造の一種)が存在せず全てを関数で処理し、関数が状態を持たないと聞くと、ついつい腰が重くなってしまいそうな気配がします。
しかし、ここでお勧めする関数型スタイルとは、C++やC#に代表されるマルチパラダイム言語においてアクセント的に使用します。そもそも、関数型言語と手続き型言語では、根本的に使用される概念も異なるため、すべてを関数型化できるわけではないですからね。

上記の例の3番目の記述法を見てみましょう。この一文、日本語として読み解くと、「even_numbers_lto(0から100までの偶数)は0から100までの整数のうち、2で割り切れる数」と解釈できます。4番目の記述法は、これをよりSQLライクに記述していると考えられます。つまり、この2文における=は、数学におけるものと同じように、等価や宣言を意味しているとも解釈できます。

前編にも記載したように手続き型言語では、プログラマの役目はコンピュータにある問題の処理方法(How -- instructions)を教えることでした。
一方、関数型言語では、Howを表すブロックが既に存在するため、これを組み合わせて(What, When)を記述することに集中することができます。この特徴から、特にビッグデータに代表されるような大量のデータを扱う場合や、手順よりも値が変化した時やユーザインタアクションがあった時にラベルの内容を変えるなど、GUIの処理を定義する際などに便利と考えられます。さらに、関数型言語の関数の特徴として副作用を持たないというものがありますが、手続き型に書くよりも宣言的に記述が行えるため、コールバックベースのGUI処理を行うよりも見通しが立ちやすいという利点もあります。

2016/2/9 23:21追記:
この意味では、リアクティブプログラミング(いい記事がなかったので、リンク先は英語版のWikipediaです)というパラダイムが考案されています。

2016年2月6日土曜日

CoreDataでdate型のattributeを使用して比較を行う

ご無沙汰しています。はざまです。新しく記事を書くのは一体何ヶ月ぶりでしょうか・・・(約1年ぶりみたいですね・・・)
さて、前置きはこのぐらいにして、本題に入りましょう。

つい先日から、Swiftを使用してiOSアプリ作成を始めたのですが、CoreDataを使用してdate型の値を扱おうとした時に少しハマった点があったので、ここにメモ代わりとして残しておこうと思います。
 以下、CoreDataの基礎は理解しているものとして記載します。

例えば、CoreDataで以下のような構造のデータを管理しているとします。
id int32,
name string,
price int32
date date

この構造のデータベースから今月追加した分の商品の情報を全件取得したいとします。CoreDataのAPIを使用すれば、次のように記述すれば目的は達成できます。

let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let request = NSFetchRequest()
let entity = NSEntityDescription.entityForName("Items", inManagedObjectContext: context)
request.entity = entity

let (this_month, next_month) = self.calculateFirstDayInMonthAndNext()
request.predicate = NSPredicate(format: "%@ <= date AND date < %@", argumentArray: [this_month, next_month])

do {
    let results = try context.executeFetchRequest(request)
...

しかし、実はこの書き方には落とし穴があります。XCodeの標準機能を使用してCoreDataのエンティティに対してモデルクラスを生成すると、date型はNSTimeInterval型としてマッピングされます。CoreDataは内部データストレージとしてSQLiteを使用しています。このSQLiteにはMySQLのdatetime型に相当する型が存在しないため、CoreDataのAPIは、NSDateに代表される日付型を、単純な基準日時からの経過時間に変換して保存します。この時、timeIntervalSinceReferenceDateプロパティを使用して変換を行うため、データの保存時にもこのプロパティを使用して変換していなければ齟齬が発生してしまい、条件にひっかからなくなってしまいます。

なお、どうしてもtimeIntervalSinceReferenceDateを使用してdate attributeの中身を上書きできない場合には、NSPredicateの中身をdate.timeIntervalSince1970などと書き換えることで、適当なNSDate型のプロパティを使用して条件指定できるようになります。既にリリース済みのアプリなどで、容易にデータ再構築ができない場合には一考する価値がありそうです。