コグノスケ


2014年 1月 3日

脱家電?

パナソニック“脱家電”路線の衝撃度 松下翁の「水道哲学」は消えるのか

えー。脱家電じゃなくて、脱黒物家電でしょう。

赤字 5兄弟が「テレビ、半導体、携帯、回路、光ピック」ならば、携帯以外は全部テレビ関連じゃないですか。脱テレビといっても過言じゃないです。

メモ: 技術系?の話は Facebook から転記しておくことにした。

編集者: すずき(更新: 2014年 3月 17日 00:24)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2014年 1月 6日

Scala でバイナリ解析

Scala にて Boolean のリストをビット列に見立てて、任意のビット数を上位ビット〜下位ビットに OR 演算し、Int のリストとして返すという処理をやりたいのです。

ビット列 (0, 1, 0, 0, 1, 1, 0, 0, 0) に対して、(1ビット取る, 3ビット取る, 5ビット取る) という処理を行って、結果として (0, 4, 24) が返る、そんなイメージです。

いきなり作るとコケたときショックがデカいので、まずは単純化して必ず 8ビットずつ取る処理で考えてみます。入力、出力は下記の通りです。

やりたいこと

(入力)
List(
  true, false, false, true, true, false, true, false,
  true, true, true, false, true, false, true, true
)

(出力)
List(154, 235)

やりたいことはシンプルなのですが、あまりスマートな書き方が思いつきません。

foreach で書く

まず思いついたのが foreach です。

foreach の場合

val a = List(
  true, false, false, true, true, false, true, false,
  true, true, true, false, true, false, true, true
)
var acc = 0
var p = 0
var b: List[Int] = List()

a.foreach { it =>
  acc <<= 1
  if (it) acc |= 1
  p += 1

  if (p >= 8) {
    b = b :+ acc
    acc = 0
    p = 0
  }
}

println(b)

動くには動きますが、8ビット読むごとにリストのコピー処理が走るという、富豪プログラミングの限界に挑戦しているようなプログラムです。

map 関数で書く

次に思い浮かぶのはmap 関数ですが、基本的に foreach と変わらないように思います。変換前と変換後が 1:1 対応するなら簡単に書けますが、n:1 対応させると一時変数の隠蔽が必要になり面倒です。

map の場合

object BooleanToByte {
  def apply(): (Boolean => Option[Int]) = {
    val o = new BooleanToByte()
    o.mapper
  }
}

class BooleanToByte() {
  private var v = 0
  private var p = 0

  def mapper(b: Boolean): Option[Int] = {
    v <<= 1
    if (b) v |= 1
    p += 1

    if (p >= 8) {
      val result = Some(v)
      v = 0
      p = 0
      result
    } else {
      None
    }
  }
}


val a = List(
  true, false, false, true, true, false, true, false,
  true, true, true, false, true, false, true, true
)

val b = a.map(BooleanToByte()).flatten

println(b)

ひとまず見た目を a.map(f) の形に近づけるためだけに頑張りました。コードがダサいというか、Scala 初心者感が丸出しというか。もっとスマートに書けるはずですが、今の私のレベルではなんとも…。

ちなみに最後の flatten は Some オブジェクトを展開して中身の値を取り出すために使っています。ついでに None も消してくれるナイスな奴です。

iterator はどうか?

最後にリストの要素を列挙するもう一つの手段、イテレータが思いつきました。foreach と異なり n:1 対応に融通が利きます。いや…融通が利くというより、自由に記述できてしまうが故に n:1 だろうと n:m だろうと、もはや何でもアリと言った方が正しいですね。

iterator の場合

val a = List(
  true, false, false, true, true, false, true, false,
  true, true, true, false, true, false, true, true
)
val it = a.iterator
var b: List[Int] = List()

while (it.hasNext) {
  var acc = 0
  var p = 0

  while (p < 8) {
    acc <<= 1
    if (it.next()) acc |= 1

    p += 1
  }
  b = b :+ acc
}

println(b)

先ほどの foreach や map のような、無理やり頑張った感はなくなったように思いますが、今度は変換元に 8 未満の要素数が残っているとき、例外がスローされてしまうという欠点もあります。

これが foreach を差し置いてベストか?と言われると、何とも言い難い、難しいですね。

編集者: すずき(更新: 2014年 1月 7日 08:56)

コメント一覧

  • よしだ 
    ふむふむ。。
    私はRubyで考えてみます! 
    (2014年01月08日 01:48:04)
  • すずき 
    (いけじからの Facebook コメント転記)
    val b = List(false, true, false, false, true, true, false, false, false)
    val c = List(1,3,5)
    に対して、case ((i,o),n)=>(i.drop(n),i.take(n)::o)._2.reverse.map{bs=>bs.map{b=>if(b) 1 else 0}.fold(0){(i,b)=>i*2+b}}.toList\nとかやりたいんですが、色々汚ないですね。 
    (2014年01月11日 14:05:26)
  • すずき 
    (Facebook コメント転記)
    やりたいことはなんとなくわかりますが、かなりゴチャゴチャしますね。
    やはりイテレータでがんばるか。 
    (2014年01月11日 14:06:07)
  • すずき 
    (いけじからの Facebook コメント転記)
    整理してこんな感じです。
    https://gist.github.com/anonymous/9d3cf430f76293b8b36c 
    (2014年01月11日 14:06:43)
  • すずき 
    (Facebook コメント転記)
    print入れまくってやっと意味がわかりました。
    foldLeftってタプル返せるんですね。応用が利きそうです。 
    (2014年01月11日 14:07:28)
  • すずき 
    >よしださん
    どの言語でも、速くて読みやすい、を目指すと難しそうですね。 
    (2014年01月11日 14:31:42)
  • IKeJI 
    Rubyで、int,short,longしか出てこないなら、
    pack、unpackだけでできないかな。 
    (2014年01月11日 17:11:25)
open/close この記事にコメントする



2014年 1月 7日

コメント

日記にコメントが書けない状態になっていましたが、復旧させました。

原因はコメント投稿内容の確認を行う際に、一時的にコメントの下書きを保存するファイルが多数のスパムにより肥大化したこと、のようです。

肝心の下書き保存ファイルが肥大化するとなぜコメントが書き込めなくなるのか?を解析しないうちに直してしまったため、真因まで追えていません。

真因が不明なままなので再発の可能性は残りますが、おそらくかなり先のことでしょう。たぶん…。

編集者: すずき(更新: 2014年 1月 7日 23:37)

コメント一覧

  • すずき 
    コメントのテスト、その 2。 
    (2014年01月07日 23:34:10)
  • すずき 
    てすと 
    (2014年03月14日 23:06:27)
  • すずき 
    また書き込めなくなった…。 
    (2014年03月14日 23:12:36)
  • すずき 
    これでどうだろう。 
    (2014年03月14日 23:30:03)
open/close この記事にコメントする



2014年 1月 8日

ポケモン恐るべし

「ポケモンバンク」配信を一時停止 アクセス殺到による接続障害緩和のため

クリスマス × ポケモン = 鯖落ち。

ポケモンは恐しい子!

メモ: 技術系?の話は Facebook から転記しておくことにした。

編集者: すずき(更新: 2014年 3月 17日 00:28)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2014年 1月 10日

Git の format-patch と am でハマった

Git で相手に変更を取り込んでもらいたいとき、git format-patch で作成したパッチをメールなどで送って、相手に git am で取り込んでもらう、という方法をよく使っています。

変なコミットメッセージを書いてしまって git format-patch と git am で思わぬ罠にハマったので紹介しておきます。

format-patch, am の概要

自分から別の人にパッチを送る時の流れを紹介します。まず自分側のリポジトリにて、

  • git init: リポジトリを作る
  • git commit: コミットを行う
  • git log: どのコミットのパッチを作るか確認(必要あれば)
  • git format-patch: パッチを作る
  • E-mail, FTP, etc...: パッチを相手に送る

パッチを送られた相手は、

  • E-mail, FTP, etc...: パッチを受け取る
  • git am: パッチを適用する

とします。

コミット(git commit)はできる

変なコミットメッセージを付けたときの format-patch, am の動きを見ます。自分のリポジトリは git_test1 にあり、パッチを受け取った相手のリポジトリは git_test2 にあるものとします。

初めに git init で自分のリポジトリ(git_test1)を作り、まずは普通のコミットメッセージを付けてコミットします。

Git のバージョン
$ git --version
git version 1.7.10.4
自分のリポジトリを作る
$ mkdir git_test1
$ cd git_test1
$ git init
Initialized empty Git repository in /home/katsuhiro/git_test1/.git/

$ touch a
$ git add a
$ git commit

(...略...)
普通のコミットメッセージでコミット
$ touch file1
$ git add file1
$ git commit

----------------------------------------------------------------------
Add: Normal commit

Normal commit message.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   file1
#
----------------------------------------------------------------------
".git/COMMIT_EDITMSG" [変換済] 12L, 297C 書込み
[master 9ec38a3] Add: Normal commit
 0 files changed
 create mode 100644 file1

次に変なコミットメッセージを付けてコミットします。

変なコミットメッセージでコミット

$ touch file2
$ git add file2
$ git commit

----------------------------------------------------------------------
Add: Abnormal commit

Abnormal commit message.

From 0000000000000000000000000000000000000000 Mon Jun 1 00:00:00 2000

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   file2
#
----------------------------------------------------------------------
".git/COMMIT_EDITMSG" [変換済] 14L, 372C 書込み
[master c4e8588] Add: Abnormal commit
 0 files changed
 create mode 100644 file2

コミットメッセージ中にある、
「From 000000(=SHA1-ID っぽいもの) Mon Jun 1 00:00:00 2000(=日付)」
という行が「変な部分」ですが、コミットの時点では特に何も起きません。

パッチ作成(git format-patch)もできる

どのコミットのパッチを作れば良いか確かめるため、git log でコミットログを確認します。

コミットログの確認
$ git log

commit c4e85884f643c0e8b107ad0f3e3b0c9fa917409b
Author: Katsuhiro Suzuki <katsuhiro@katsuster.net>
Date:   Sat Jan 11 12:24:17 2014 +0900

    Add: Abnormal commit

    Abnormal commit message.

    From 0000000000000000000000000000000000000000 Mon Jun 1 00:00:00 2000

commit 9ec38a315e964241f69897728b11f2451eafd700
Author: Katsuhiro Suzuki <katsuhiro@katsuster.net>
Date:   Sat Jan 11 12:22:49 2014 +0900

    Add: Normal commit

    Normal commit message.

commit f3e44926c8f50c557841a157fda89f3fab4aaab3
Author: Katsuhiro Suzuki <katsuhiro@katsuster.net>
Date:   Sat Jan 11 12:22:21 2014 +0900

    Init.

f3e449...(Init), 9ec38a...(Normal), c4e858...(Abnormal) というログになっていますので、Init から後にある Normal と Abnormal、2つのコミットのパッチを作ります。

パッチ作成
$ git format-patch f3e44926c8f50c557841a157fda89f3fab4aaab3
0001-Add-Normal-commit.patch
0002-Add-Abnormal-commit.patch
Normal の方のパッチ

From 9ec38a315e964241f69897728b11f2451eafd700 Mon Sep 17 00:00:00 2001
From: Katsuhiro Suzuki <katsuhiro@katsuster.net>
Date: Sat, 11 Jan 2014 12:22:49 +0900
Subject: [PATCH 1/2] Add: Normal commit

Normal commit message.
---
 0 files changed
 create mode 100644 file1

diff --git a/file1 b/file1
new file mode 100644
index 0000000..e69de29
--
1.7.10.4
Abnormal の方のパッチ

From c4e85884f643c0e8b107ad0f3e3b0c9fa917409b Mon Sep 17 00:00:00 2001
From: Katsuhiro Suzuki <katsuhiro@katsuster.net>
Date: Sat, 11 Jan 2014 12:24:17 +0900
Subject: [PATCH 2/2] Add: Abnormal commit

Abnormal commit message.

From 0000000000000000000000000000000000000000 Mon Jun 1 00:00:00 2000
---
 0 files changed
 create mode 100644 file2

diff --git a/file2 b/file2
new file mode 100644
index 0000000..e69de29
--
1.7.10.4

こんなパッチができます。

だがパッチ適用(git am)ができない

パッチ適用の前に、パッチを受け取った相手のリポジトリを作ります。

相手のリポジトリの作成
$ mkdir git_test2
$ cd git_test2
$ git init
Initialized empty Git repository in /home/katsuhiro/git_test2/.git/

パッチを受け取った相手は git am でパッチ適用します。

パッチを受け取った
$ mv ../git_test1/000* ./
Normal のパッチ適用、成功
$ git am 0001-Add-Normal-commit.patch
Applying: Add: Normal commit
applying to an empty history

順調です。しかし変なコミットのパッチを適用しようとすると、

Abnormal のパッチ適用、失敗
$ git am 0002-Add-Abnormal-commit.patch
Patch is empty.  Was it split wrong?
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".

このように止まってしまいます。

なんでこんなことした?

Git で開発しているオープンソースプロジェクトでは、メーリングリストに git format-patch のパッチを貼りつけてあることが良くあります。

あるソフト A に最新バグフィクスをバックポートする際に、コミットメッセージを自分で書く代わりに、ソフト A の開発プロジェクトのメーリングリストの文面をコピペしたんですね。

このとき format-patch のヘッダ、つまり From 00000... の部分までコミットログに貼り付けてしまったがために、本来はコミットメッセージであるはずの部分を git am がパッチファイルのヘッダと勘違いして、パッチが変だ、中身が無いよ!と怒っているようです。

編集者: すずき(更新: 2014年 1月 11日 12:52)

コメント一覧

  • たけうち 
    今日、この問題にハマって困っていたところ、
    このページに助けられました! 
    (2014年01月17日 22:34:51)
  • すずき 
    >たけうちさん
    お役に立てて良かったです。
    気づけば当然なのですが、意外と気づかないです…。 
    (2014年01月18日 00:16:35)
open/close この記事にコメントする



2014年 1月 11日

さすが Intel

【イベントレポート】IntelのSDカード型コンピュータ「Edison」の詳細が判明 〜512KBメモリ、2GBストレージ、無線機能などを詰め込む - PC Watch

Quark が去年発表したモデルから 2倍くらい速くなってることと、実際に SD カードサイズのボードを作ってしまう辺りが流石 Intel って感じ…。

メモ: 技術系?の話は Facebook から転記しておくことにした。

編集者: すずき(更新: 2014年 3月 17日 00:28)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2014年 1月 12日

Intel と組み込み系

Intel は組み込みで勝っているとは言えないけど、なぜだろう。

  • CPU の性能が合わないから?
  • IP 売りしてないから?
  • PC/AT じゃないから?

性能?

Atom は電力/性能比はイマイチかもしれないけど結構速い。多くの SoC ベンダが採用する省電力プロセスの ARM Cortex A9 ならいい勝負らしい。

ARM社のデュアルコアCortex-A9、低消費電力版でも性能はAtomを上回る

IP 売り?

Atom は以前 IP 売りしたみたいだけど、売れた様子はない。

元麻布春男の週刊PCホットライン - IntelとARMが目指す、それぞれの隣の芝生

PC/AT?

次世代機の PS4, XBox One は x86 かつ PC/AT ではない組み込み機器だけど、勝ったのは Intel じゃなくて AMD だった。

PS4もXbox OneもAMDはいってる:2013年末、いよいよ“新生AMD”が新たなステージへ

単純な問題ではないのだろうなあ…。

メモ: 技術系?の話は Facebook から転記しておくことにした。

編集者: すずき(更新: 2014年 3月 17日 00:36)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2014年 1月 16日

エンジニアなら

エンジニアを成長させる、たった6つの指針。|クックパッド CTO 橋本健太に訊く![前編]

一番わかりやすいコミュニケーションは、「動くもの」と「数字」。エンジニアならば、「これだよ」と作って見せてみる。
(記事中「議論のための議論はしない〜動くものと数字で示す」より引用)

エンジニアなら、ほんとこれしかないと思います。「本番で動くモンはこれ、どう?便利でしょ?」って見せるんです。

ここがダメなら、どんな資料も説明も意味無いでしょう?だってテメーの商品のことはわかった、だが気に入らん、と言われてるに等しいのだから。

メモ: 技術系?の話は Facebook から転記しておくことにした。

編集者: すずき(更新: 2014年 3月 17日 00:39)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2014年 1月 19日

大回り乗車

Facebook に「路線がループしているときの運賃の扱いがよくわからん」的なことを書いたら「大回り乗車」を教えていただいたので、ちょっと調べてみました。

電車の基本ルールとしては、乗る時に使った切符の範囲外に行ってはいけません。例えば、下記路線で B → C の切符で乗り D まで行った場合、C → D の運賃を追加で払う必要があります。


A - B - C - D

しかし途中に環状線が入ってたらどうなるでしょう?下記路線で B 駅から D 駅に行く切符で、B → A → E → F → G → C → D と乗る場合です。一見すると B 駅から G 駅までの運賃も払う必要がありそうです。


A - B - C - D
|       |
E - F - G

ところがこの場合、ある条件を満たせば B 駅から D 駅の切符で乗車できます。これがいわゆる「大回り乗車」です。

大都市圏近郊区間

路線がループしている大都市圏には、運賃計算の特殊ルールが存在します。JR の説明によると「実際に乗った経路に関わらず、最も安い経路で運賃が計算される」というルールです。

このルールを逆手にとって、1駅分の切符を購入し、出発駅→目的の駅→出発駅の隣接駅、と乗ること(大回り乗車)が可能です。条件としては、

  • 圏内を出てはならない
  • 同じ駅を 2度通れない(交差はダメなのか??)
  • 途中下車できない
  • 定期券は使えない
  • (条件ではないが)駅員に乗車経路を聞かれることがある

です。駅から出ない乗り方、例えば電車や駅の写真を撮りたい人、電車に乗るのが好きな人に向いていますね。駅の外に出たい場合には使えませんけどね。

鉄道関連の法律

ちなみに、不正乗車の罰については、鉄道営業法という古い法律(明治三十三年施行)に述べられているようです。罰則を述べていそうなところを摘んでみると、

第十八条の二
有効ノ乗車券ヲ所持セス又ハ乗車券ノ検査ヲ拒ミ又ハ取集ノ際之ヲ渡ササル者ハ鉄道運輸規程 ノ定ムル所ニ依リ割増賃金ヲ支払フヘシ
第二十九条
鉄道係員ノ許諾ヲ受ケスシテ左ノ所為ヲ為シタル者ハ五十円以下ノ罰金又ハ科料ニ処ス
第四十二条
左ノ場合ニ於テ鉄道係員ハ旅客及公衆ヲ車外又ハ鉄道地外ニ退去セシムルコトヲ得

要は、運賃チョロまかすなんてナメた真似しようもんなら「割増料金を取る」「罰金刑」「電車から叩きだす」ぞ、わかったかこの野郎。ということですね。

個人的には、罰金 50円(※)の辺りに時代を感じます。さすが明治施行の法律です。

(※)罰金 50円なら超安いしw余裕wwってのは思い違いで、実際には 1万円か 2万円になるようです(罰金等臨時措置法)。
JR の割増料金も洒落にならないし(鉄道運輸規程 第十九条)、不正乗車はやめた方が良いですねー。

編集者: すずき(更新: 2014年 1月 20日 03:13)

コメント一覧

  • hdk 
    大回り乗車、東京から埼玉、栃木、茨城、千葉を回って東京に戻るのをやったことがあります。何時間もかけて大回りすると、途中でちょっと改札の外に出たくなりますねw
    http://www.e-hdk.com/diary/d201010a.html#02 
    (2014年01月20日 23:12:46)
  • すずき 
    >hdk さん
    うは、すごい大回り。良いですね。
    私もせっかく大都市圏(大阪)に居るので、景色の良い季節に環状線でやってみようかな。 
    (2014年01月21日 00:51:56)
open/close この記事にコメントする



2014年 1月 20日

折り返し乗車は不可

「大回り乗車」ができる区間であっても、同じ経路を折り返して 2度通ると不正乗車になります。下記路線で B 駅から D 駅に行く切符で、B → A → B → C → D と乗ることはできません。B → A → B の分を払わずに乗っているためです。


A - B - C - D
|       |
E - F - G

どうしても B → A → B → C → D という経路で行きたいなら、どこかで B → A → B の運賃を追加で払う必要があります。

ええ?そんなのどうやって払うの?と一瞬考えましたが、A 駅で一度降りて再び乗れば特に問題ないですね。面倒くさいけど。

折り返せると便利?

折り返したら何が嬉しいか?というと、

  • 隣接の A 駅が主要駅(快速停車)かつ近い
  • 最寄りの B 駅がヘボ駅(快速通過)
  • 目的地の D 駅が遠い
  • A 駅方向への電車がすぐ来る
  • D 駅方向への電車がしばらく来ない

という条件のとき、通常の経路 B →(普通)→ A → C → D を使って行くより、B →(普通)→ A →(快速)→ B → C → D と乗る方が早く着くことがあるので、嬉しいのです。

例えば、つくばに居た時の、研究学園駅(車停めたい時の最寄り)とつくば駅(主要)ですとか、今住んでる摂津富田駅(最寄り)と高槻駅(主要)が当てはまります。

実際には良くても 5〜15分早く着くだけでしょうし、そのために追加運賃と乗り換えの手間を掛けることを考えると効果は微妙…。よほど急いでいる時でない限り、積極的にはやりたくないですね。

編集者: すずき(更新: 2014年 1月 20日 03:26)

コメント一覧

  • IKeJI 
    研究学園駅とつくば駅を想像すると、いまいちわからないと思いますが、座れるというメリットがあったりしません? 
    (2014年01月20日 05:14:40)
  • すずき 
    >IKeJI さん
    始発駅が隣にある場合、座れるというメリットもありますね。
    Wikipedia には横浜駅→みなとみらい駅の例が載っていました。
    http://ja.wikipedia.org/wiki/%E4%B8%8D%E6%AD%A3%E4%B9%97%E8%BB%8A 
    (2014年01月20日 09:16:25)
open/close この記事にコメントする



2014年 1月 21日

Scala まだまだトライ中

最近は Scala とお友達ですが、未だに文法やら動きがワケわかりません。俺、この言語向いてないのかなー、なんて思いつつもチマチマと書いています。

そんな状態なので、Scala っぽくない書き方をして後で気づいたり、間違えてバグらせたりで、気づいたら「死んでる…」となっていることが多いです。

グチャグチャになる前にテストを書こう、後でリファクタリングするときの役にも立つ、と思い立ちました。

思い立ったまでは良かったものの、Scala のメジャーなテストフレームワークについて何も知りません。Java でいう JUnit のようなスタンダードがあるととても助かりますが…。

ScalaTest がおすすめ?

Scala のテストを調べてみると ScalaTest というのが有名処のようです。ScalaTest では「テスト」ではなく「スペック」というようです。

私の理解だと「テストのためにプログラムの動作結果の羅列するのはおかしいんよ。振る舞いや期待する仕様を書けばテストになる方が自然なんよー。」というスタイルみたいです。振る舞いを書くところから開発するスタイルを BDD(Behavior Driven Development、Wikipedia ビヘイビア駆動開発の項)と言うそうです。へぇー。

思想は素晴らしいのですが、ScalaTest は従来の JUnit のような「テスト」から、振る舞いを書く「スペック」まで、何でも出来るようにしてしまったせいで、ゴチャゴチャです。初心者には何を使えば良いか判断がつきません。

とりあえず、良くわからないままですけど FlatSpec で書くことにしました。

ScalaTest と IntelliJ IDEA

私の Scala プログラミング環境は IntelliJ IDEA + Scala Plugin ですので、これと ScalaTest を連携させたいと思います。

といっても何も難しいことは無く、下記のような FlatSpec のスペッククラスを書いたら Project のツリー図から右クリックして、Run を選ぶ、ほぼそれだけです。

スペックの例

import org.scalatest.FlatSpec

class HogeSpec extends FlatSpec with Matchers {
  val hoge = "hoge"
  val fuga = "fuga"
  "hoge" should "starts with 'h'" in {
    hoge.startsWith("h") should be (true)
  }
}

テスト結果は IntelliJ IDEA の結果表示ウインドウに、ツリー状に見やすく配置されます。IntelliJ IDEA + Scala Plugin + ScalaTest のトリオはなかなか素敵です。

複数テストを同時に行うには?

スペックを書く方法はわかりましたが、1度に複数のスペックを動作させるにはどうしたら良いのでしょう?

さらに言うと 1度に複数のスペックを動作させた結果が、IntelliJ IDEA の結果表示ウインドウにも出て欲しいですが、どう書いたら良いのでしょう??さっぱりわかりません…。

まとめ用のクラス AllSpec から、先ほどの HogeSpec と新たに FugaSpec を 1度に動作させるという想定で、思いつく手だてを試してみたいと思います。

execute してみる

まずは素直に、まとめ用のクラスで各クラスのオブジェクトを作り execute() を呼びました。

execute でまとめる

import org.scalatest.FlatSpec

class HogeSpec extends FlatSpec with Matchers {
  val hoge = "hoge"
  "hoge" should "starts with 'h'" in {
    hoge.startsWith("h") should be (true)
  }
}

class FugaSpec extends FlatSpec with Matchers {
  val fuga = "fuga"
  "fuga" should "starts with 'f'" in {
    fuga.startsWith("f") should be (true)
  }
}

class AllSpec extends FlatSpec {
  new HogeSpec().execute()
  new FugaSpec().execute()
}
execute でまとめた結果
HogeSpec:
hoge
- should starts with 'h'
FugaSpec:
fuga
- should starts with 'f'
AllSpec:
AllSpec

コンソールへの出力ではまとまって実行されているように見えますが、入れ子になっていないし、IntelliJ IDEA は「NO TEST」という悲しい結果を表示します。ダメですね。

シングルトンの関数

次に HogeSpec と FugaSpec をシングルトンにしてスペックは関数内に入れ、まとめ用のクラスから呼んでみました。

シングルトンでまとめる

import org.scalatest.FlatSpec

object HogeSpec extends FlatSpec with Matchers {
  def hogeTest {
    val hoge = "hoge"
    "hoge" should "starts with 'h'" in {
      hoge.startsWith("h") should be (true)
    }
  }
}

object FugaSpec extends FlatSpec with Matchers {
  def fugaTest {
    val fuga = "fuga"
    "fuga" should "starts with 'f'" in {
      fuga.startsWith("f") should be (true)
    }
  }
}

class AllSpec extends FlatSpec {
  HogeSpec.hogeTest
  FugaSpec.fugaTest
}
シングルトンでまとめた結果
AllSpec:
AllSpec

何も出なくなりました。ダメだこりゃ…。

トレイトで Mix-in

最後に HogeSpec と FugaSpec をトレイトにして、まとめ用のクラスに Mix-in してみました。

トレイトでまとめる

import org.scalatest.FlatSpec

trait HogeSpec extends FlatSpec with Matchers {
  val hoge = "hoge"
  "hoge" should "starts with 'h'" in {
    hoge.startsWith("h") should be (true)
  }
}

trait FugaSpec extends FlatSpec with Matchers {
  val fuga = "fuga"
  "fuga" should "starts with 'f'" in {
    fuga.startsWith("f") should be (true)
  }
}

class AllSpec extends FlatSpec 
with HogeSpec
with FugaSpec {
  //empty
}
トレイトでまとめた結果
AllSpec:
hoge
- should starts with 'h'
fuga
- should starts with 'f'
AllSpec

コンソールへの出力ではまとまって実行されていて、IntelliJ IDEA にもテスト項目が認識されています。これはなかなか良さそうです。しかし、

  • 異なるスタイルのスペックを混ぜられない(FlatSpec と FunSpec など)
  • HogeSpec や FugaSpec を単独で実行できない

という欠点もあってイマイチです。でも良い方法がわからんす、困ったもんです。

編集者: すずき(更新: 2014年 1月 21日 02:59)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2014年 1月 24日

Scala でバイナリ解析 2

前回の続き(2014年 1月 6日の日記参照)です。

おさらいすると、Boolean のリストをビット列に見立てて、任意のビット数を上位ビット〜下位ビットに OR 演算し、Int のリストとして返すという処理をします。

ビット列 (0, 1, 0, 0, 1, 1, 0, 0, 0) に対して、(1ビット取る, 3ビット取る, 5ビット取る) という処理を行って、結果として (0, 4, 24) が返るイメージです。

やりたいこと

(入力 1)
List(
  true, false, false, true, true, false, true, false,
  true, true, true, false, true, false, true, true
)

(入力 2)
List(1, 2, 3, 4, 5, 6)

(出力)
List(1, 0, 6, 11, 21)

ビット列に相当するリストが入力 1、何ビットずつまとめるか、という別のリストが入力 2 になります。

前回は特に触れませんでしたが、入力 1 と入力 2 の合計が異なるときの扱いについては悩みどころです。余ったリストを返すのが一番親切かもしれませんが、出力が 3つになってしまいます…。

今回は、一番楽に(入力 1>入力 2)なら例外スロー、(入力 1<入力 2)なら切り捨て、としています。

前回の反省

前回使った map では(入力:出力)の要素数を(1:1)にする必要があります。そのため (n:1)にしたければ、入力はあるが、出力はない「穴が空いた」状態を表現する必要が生じます。

Option 型を使って、値は Some(値), 穴は None とし、最後に flatten を呼んで None を全部捨てましたが、あまり効率的とは思えません。

foldLeft はどうか?

入出力の要素数が一致していなくても構わないのが foldLeft です。

foldLeft の場合

object BooleanToInt {
  def apply(g: List[Int]): ((List[Int], Boolean) => List[Int]) = {
    val o = new BooleanToInt(g)
    o.folder
  }
}

class BooleanToInt(g: List[Int]) {
  private var v = 0
  private var p = 0
  private var mp = g(0)
  private var gg = g.drop(1)

  def folder(a: List[Int], b: Boolean): List[Int] = {
    v <<= 1
    if (b) v |= 1
    p += 1

    if (p >= mp) {
      val result = a :+ v
      v = 0
      p = 0
      mp = gg(0)
      gg = gg.drop(1)
      result
    } else {
      a
    }
  }
}


val a = List(
  true, false, true, false,
  true, true, false, false,
  true, false, false, true,
  false, true, true, false
)

val b = List(1, 2, 3, 4, 5, 6)

val c = a.foldLeft(List[Int]())(BooleanToInt(b))

println(c)

コードが Scala 素人くさいのはさておき、Some とか None が出てこない分、map より良さそうです。

foldLeft の別形式

val c = (List[Int]() /: a)(BooleanToInt(b))

初期値のリストが長いときは foldLeft を /: で書く手もあります。個人的には右側が被演算子になると読みづらいし、使うことはないですね…。

編集者: すずき(更新: 2014年 1月 25日 12:57)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2014年 1月 25日

演算子はメソッドだ

Scala の特徴の 1つは引数が 1つのメソッドを演算子のように呼び出せることです(Ruby も同じですね)。背景には、基本型を廃して「全てオブジェクト」とする、言語の文法から演算子を廃する、という設計哲学があるようです。

しかし演算子の優先順位まで消滅して 1 + 2 * 3 = 9 になってしまうと(全て左結合させると ((1 + 2) * 3) になる)非常に混乱しますので、コンパイラが特殊なメソッド名だけ結合の優先度を変更します。

特殊なメソッド名を付けると優先度が変わる

class MyInt(v: Int) {
  val i = v

  def +(a: MyInt): MyInt = {
    new MyInt(i + a.i)
  }

  def *(a: MyInt): MyInt = {
    new MyInt(i * a.i)
  }

  def plus(a: MyInt): MyInt = {
    new MyInt(i + a.i)
  }

  def mult(a: MyInt): MyInt = {
    new MyInt(i * a.i)
  }
}

val a = new MyInt(1)
val b = new MyInt(2)
val c = new MyInt(3)

val r1 = a + b * c
val r2 = a plus b mult c

println("r1:" + r1.i)
println("r2:" + r2.i)
r1:7
r2:9

演算子もメソッドの一種ではありますが、上記のように旧来の演算子と同名のメソッドはやはり、優先度において特別です。単純に別名のメソッドで置き換えると、結合順序が狂います。

こうまでして文法上から演算子を廃したのはなぜだろう?誰かにメリットがあるはずなのだけど、何が嬉しいのかわからん…。

Scala のシフト演算と比較

優先度と言えば Scala は優先度付けのルールが Java と違います。例えば、Java はシフト演算が比較より優先ですが、Scala は優先度が同じです。

シフトと比較が同一優先度
(Java なら OK、Scala では NG)

scala> 1 < 1 << 2
<console>:8: error: value << is not a member of Boolean
              1 < 1 << 2
                    ^

(本来やりたいこと)

scala> 1 < (1 << 2)
res1: Boolean = true

上記の例のように、Scala で「(1) < (1 << 2)」のつもりで「1 < 1 << 2」と書くと、左から結合されて「(1 < 1) << 2」になって型エラー(※)が起き、ちょっと不思議な気持ちになります。

あえて Java と違う優先度にしたのはなぜだ…。

(※)1 < 1 は Boolean を返し、Boolean クラスは左シフトメソッド << を定義していないため、エラーになる。

編集者: すずき(更新: 2014年 1月 25日 15:08)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2014年 1月 26日

Scala の中間記法の優先度

Scala と Java でシフト演算子の優先度が違うのは、決して嫌がらせではなくて「Scala の優先度は、メソッド名の先頭の 1文字で決まる」ルールから来ているようです。

素人目線だと 2文字見れば良かったんじゃ…?と思いますが、そうすると何か大変なことがあったのでしょう。たぶん。

それはさておき、Scala で中間記法を使ったときの優先度は、低い順から、


(all letters) 注: アルファベットとか
|
^
&
< >
= !
:
+ -
* / %
(all other special characters) 注: ~ とか ? とか

注: Scala 言語仕様 Version 2.8 の 6.12.3 Infix Operations より

となっています。シフト(<<)やビット演算(& |)は、比較演算(== !=)よりも「優先度が低い」はずです。

普通の & と =
scala> 1 & 2 == 5
<console>:8: error: overloaded method value & with alternatives:
  (x: Long)Long <and>
  (x: Int)Int <and>
  (x: Char)Int <and>
  (x: Short)Int <and>
  (x: Byte)Int
 cannot be applied to (Boolean)
              1 & 2 == 5
                ^


(& が優先なら)

scala> (1 & 2) == 5
res40: Boolean = false

(== が優先なら、こっちが期待値)

scala> 1 & (2 == 5)
<console>:8: error: overloaded method value & with alternatives:
  (x: Long)Long <and>
  (x: Int)Int <and>
  (x: Char)Int <and>
  (x: Short)Int <and>
  (x: Byte)Int
 cannot be applied to (Boolean)
              1 & (2 == 5)
                ^

ビット演算の方が優先度が低いので、上記の例のように「1 & (2 == false)」と解釈されて、型エラーになります。

謎の < と =
scala> 1 < 2 == false
res41: Boolean = false


(< が優先なら)

scala> (1 < 2) == false
res42: Boolean = false

(== が優先なら、こっちが期待値)

scala> 1 < (2 == false)
<console>:8: error: overloaded method value < with alternatives:
  (x: Double)Boolean <and>
  (x: Float)Boolean <and>
  (x: Long)Boolean <and>
  (x: Int)Boolean <and>
  (x: Char)Boolean <and>
  (x: Short)Boolean <and>
  (x: Byte)Boolean
 cannot be applied to (Boolean)
              1 < (2 == false)
                ^

比較演算の方が優先度が低いので、「1 < (2 == false)」と解釈され、型エラーになるかと思ったら、なりません。意味がわかりません。

編集者: すずき(更新: 2014年 1月 26日 03:48)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2014年 1月 28日

嬉しいこと、嫌なこと

言われると嬉しいこと、嫌なことは人によって様々かと思います。

僕は、下に行くほど、やる気が減ります。

  • 能動「こんなのできた!」
  • 受動「何したらいい?」
  • 褒め「いいね!」
  • 提案「こんな風に良くしよう」
  • 批判「どうしてこうなった?」
  • 催促「できた?」
  • 無視「わからない」「知らない」「小麦粉か何かだ」
  • 評論「これだから〜はダメ」
  • 暴力「死んでもやれ」「(机バンバン)」

評論家は嫌い

内容が提案や批判と似ていても、
「あーあ、わかってないなー。ビシッと俺様が正論でシメてやらんとダメだな!」
的な言い放し無責任感が備わると評論、…というイメージで使ってます。

評論されると、内容はそれなりでも一番やる気が萎えます。なんでかわからんけど。

メモ: 技術系?の話は Facebook から転記しておくことにした。

編集者: すずき(更新: 2014年 3月 17日 00:42)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2014年 1月 29日

ノート PC と SeaMonkey

家のノート PC で SeaMonkey を使っていると、画像が一部表示されないサイトがあります。

色んなサイトが該当しますが、例えば、らば Q だと何の嫌がらせなのか、記事中の画像「だけ」表示されません。その一方で、背景や、らば Q のバナーは出ますので、サイトやネットワークの問題ではなさそうです。

また IE や Chrome で見ると画像が表示されますので、ファイアウォールなどに遮断されているわけでもなさそうですし、ノート PC とは別のデスクトップ PC(Windows 7 64bit 版)の SeaMonkey で見ても画像が表示されますから、SeaMonkey 自体の問題でもなさそうです。

SeaMonkey の Profile Manager を起動(引数に -p を付けて起動する)して、新しく Profile を作ると画像が表示されたので、どうやらノート PC の SeaMonkey の Profile に原因がありそうです。

Profile を直そう

新規 Profile から設定をやり直すのも面倒くさいし、今まで使っていた Profile を何とかしようとして、キャッシュクリアとか、Cookie 全消しとか、色々やってみたものの、一向に改善されません。困りました。

もう Profile がぶっ壊れてもいいや、という覚悟で Profile のディレクトリから適当に「blocklist.xml」ファイルと「adblockplus」「safebrowsing」ディレクトリを消してみたら、見事に復活しました。うーん、わけわからんな…。

編集者: すずき(更新: 2014年 1月 30日 02:40)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



こんてんつ

open/close wiki
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報