C# の double の %

C# 5.0 の double の % がなんか仕様とは異なる値を返す気がするという話。

Download C# Language Specification 5.0 from Official Microsoft Download Center C# 5.0 の言語仕様によれば、

7.8.3 Remainder operator

  • Floating-point remainder:
double operator %(double x, double y);

z is the result of x % y and is computed as x - n * y,
where n is the largest possible integer that is less than or equal to x / y.

とのことなので、n = Floor(x / y) のハズ...。でも実際は Truncate っぽい動作をする。

static void ReminderOperatorDoubleFloor()
{
    var x = -5.2;
    var y = 2.0;
    var n = System.Math.Floor(x / y);
    var z = x - n * y;
    Console.Out.WriteLine("FLOOR: x = {0}, y = {1}, n = {2}, z = {3}, x % y = {4}", x, y, n, z, x % y);
    // => FLOOR: x = -5.2, y = 2, n = -3, z = 0.8, x % y = -1.2
}

static void ReminderOperatorDoubleTruncate()
{
    var x = -5.2;
    var y = 2.0;
    var n = System.Math.Truncate(x / y);
    var z = x - n * y;
    Console.Out.WriteLine("TRUNCATE: x = {0}, y = {1}, n = {2}, z = {3}, x % y = {4}", x, y, n, z, x % y);
    // => TRUNCATE: x = -5.2, y = 2, n = -2, z = -1.2, x % y = -1.2
}

static void ReminderOperatorInt()
{
    var x = -52;
    var y = 20;
    var n = x / y;
    var z = x - (x / y) * y;
    Console.Out.WriteLine("INT: x = {0}, y = {1}, n = {2}, z = {3}, x % y = {4}", x, y, n, z, x % y);
    // => INT: x = -52, y = 20, n = -2, z = -12, x % y = -12
}

で、実際どんな式が使われてるの、というと Math.IEEERemainder(Double, Double) Method (System) | Microsoft Docs の Remarks に書かれているが、Abs を使った上で Floor して符号は後から追加している。でも仕様には Abs 使うなんて書いてないじゃん!*1

# 蛇足ですが C++03 では double の % 演算は定義されておらずコンパイルできない。

*1:7.8.2 Division operator の整数の / 演算子のところには "absolute value" うんぬんとの記述がみられるので、やはり % のほうの記述は書き忘れであるように思う

C++11 の正規表現ライブラリの曖昧さ

libstdc++ の regex が実装されたそうです。

そこで、仕様の曖昧な点(2点)を調査して纏めました。

GitHub Pages 機能を使ったことが無かったので、試しに使ってみようということで上記のところで公開してあります。

gh-pages ブランチの中で symlink を作っても、GitHub Pages で公開されたページでは symlink は辿ってくれないようでした。そのため、ちょっとだけ修正が必要でした。

正規表現で [a-[:alpha:]] の意味って?

C++11 の仕様に対する疑問

C++11 の正規表現 (std::regex_constants::ECMAScript 時) の仕様は ECMA-262 の文法を次のように微妙に変更したものとなっています。


ClassAtom ::
-
ClassAtomNoDash
ClassAtomExClass
ClassAtomCollatingElement
ClassAtomEquivalence
ClassAtomExClass ::
[: ClassName :]
ClassAtomCollatingElement ::
[. ClassName .]
ClassAtomEquivalence ::
[= ClassName =]
ClassName ::
ClassNameCharacter
ClassNameCharacter ClassName
ClassNameCharacter ::
SourceCharacter but not one of "." "=" ":"
(ドラフト n3337.pdf より)

そして ECMA-262 の正規表現の文字クラス周りの文法はだいたい以下のような感じになっています。


CharacterClass ::
[ [lookahead ∉ {^}] ClassRanges ]
[ ^ ClassRanges ]
ClassRanges ::
[empty]
NonemptyClassRanges
NonemptyClassRanges ::
ClassAtom
ClassAtom NonemptyClassRangesNoDash
ClassAtom - ClassAtom ClassRanges
NonemptyClassRangesNoDash ::
ClassAtom
ClassAtomNoDash NonemptyClassRangesNoDash
ClassAtomNoDash - ClassAtom ClassRanges
ClassAtom :: (ここは C++11 の仕様で変更される)
-
ClassAtomNoDash
ClassAtomNoDash ::
SourceCharacter but not one of \ or ] or -
\ ClassEscape
ClassEscape ::
DecimalEscape
b
CharacterEscape
CharacterClassEscape
(Standard ECMA-262 より)

ここで、強調した部分、つまり以下の部分


ClassAtom ::
ClassAtomExClass
NonemptyClassRanges ::
ClassAtom - ClassAtom ClassRanges
NonemptyClassRangesNoDash ::
ClassAtomNoDash - ClassAtom ClassRanges

を素直に解釈すると、例えば次のような正規表現は文法上正しいということになります。

[a-[:alpha:]]
[[:alpha:]-a]
[[:alpha:]-[:alpha:]]

文法上は正しいといっても、何を表現しているのか意味がよくわかりません。

現実の実装では?

例えば、Boost C++ Libraries ではテストケース (test_sets.cpp) 内で、以下のようなテストをしており、

   TEST_INVALID_REGEX("[a-[:alpha:]]", boost::regex::extended);

問題となる正規表現を記述できないことを確かめています。

また、OS X Mountain Lion の clang-425.0.28 では、

#include <regex>
#include <iostream>

int main() {
	std::locale::global(std::locale("C"));
	std::regex re("[a-[:alpha:]]");
	std::cout << std::boolalpha << regex_match("a", re) << std::endl;
	std::cout << std::boolalpha << regex_match("b", re) << std::endl;
	return 0;
}

上記のソースを -std=c++11 -stdlib=libc++ でコンパイルして実行すると


true
false
が出力されます。なにが起こったのかよくわかりません。

また、Visual Studio 2012 Express では上記のソースを実行すると std::regex のコンストラクタ内で std::regex_error 例外が .code() == std::regex_constants::error_range で throw されます。

まとめ

というわけで、この問題は C++11 の仕様の間違いとして解釈するのが良いと思います。

zshのデフォルトの補完の定義を使わないことにした

zshで以下のように設定しておくと

autoload -Uz compinit
compinit

様々な補完がデフォルトで定義されますが、ほとんどの定義は自分には不要であることが最近分かってきました。

むしろ

make ファイル名

と実行したいのにファイル名を補完できないなど、不便な点のほうが目立ちます。

compinit を行わなければデフォルトの補完も定義されないので上記のような不便はないのですが、その代わり補完候補に色が付かなくなってしまうのでそれはそれで不便です。

そこで、compinit を実行した上で不要な定義をばっさり削除することにしました。

autoload -Uz compinit
compinit

# ほとんどの補完の定義を削除する。
# special contexts の定義のみ残す (see: man zshcompsys)
fix_comp_assoc() {
	local var=$1
	shift
	for key in "$argv[@]"; do
		case $key in
		-redirect-,\<,*) unset "${var}[$key]";;
		-redirect-,\>,*) unset "${var}[$key]";;
		-value-,-*) ;;
		-value-,*) unset "${var}[$key]";;
		-*) ;;
		*) unset "${var}[$key]";;
		esac
	done
}
fix_comp_assoc _comps        "${(k)_comps[@]}"
fix_comp_assoc _services     "${(k)_services[@]}"
fix_comp_assoc _patcomps     "${(k)_patcomps[@]}"
fix_comp_assoc _postpatcomps "${(k)_postpatcomps[@]}"

compinit では、補完する時にどのようなコンテキストでどのコマンドを利用するという一覧は _comps, _services, _patcomps, _postpatcomps という連想配列に格納されるようです*1。そこで、それらの定義の中から special contexts の定義以外をばっさり削るようにしました。

これで

make ファイル名

も実行可能になり、快適zshライフを送ることができそうです。

# しかし、もっと良いやりかたがありそうなものですが…

*1:これらの変数のデフォルトの内容は ~/.zcompdump へダンプされているのでそこで確認するのが楽です

Storyboardsを使っている時のUITableViewのdequeueReusableCellWithIdentifier:の挙動

この情報に行き当たるのに苦労したのでメモ

https://developer.apple.com/library/ios/#releasenotes/Miscellaneous/RN-AdoptingStoryboards/_index.html#//apple_ref/doc/uid/TP40011297-CH1-DontLinkElementID_5

はてなブックマーク/COOKPAD画像表示(Chrome拡張)

はてなブックマーク/COOKPAD画像表示 - Chrome Web Store

はてなブックマークCOOKPADのレシピをブックマークしている場合に、レシピの写真を一緒に表示してくれる拡張です。先日ふと思い立って作ってみました。

Greasemonkeyでも書ける内容の拡張ですが、Chrome拡張として作りました。なんとなく。

zshをtcshなキーバインドで

ログインシェルとしてはもう十何年も tcsh をずっと使って来ていたのですが、All about Ruby on Rails & Data recovery software を素直に動作させるのが難しかったので zsh へ移行することを決意しました*1

【連載】漢のzsh | マイナビニュースを参考にしてだいたいは違和感なく移行できたのですが、最後までなかなか馴染まなかったのがキーバインディングでした。

最近ようやく tcsh とほぼ同じ感じになってきたので、どう設定したのかを以下にメモしておきます。

基本は Emacs バインディング

tcsh は基本的に emacs っぽいキーバインドなので、まずは全体的に emacs っぽくしました。

bindkey -e

M-p と M-n を変更

tcsh の M-p と M-n にはカーソルより左側が一致する履歴をどんどん表示する機能が割り当てられています。そこで zsh では以下のようにしました。

autoload history-search-end
zle -N history-beginning-search-backward-end history-search-end
zle -N history-beginning-search-forward-end history-search-end
bindkey "^[p" history-beginning-search-backward-end
bindkey "^[n" history-beginning-search-forward-end

M-f や M-b などの単語を扱う機能の調節

この機能が一番のくせ者でした。結論から言えば、以下を ~/.zshrc に書き、

autoload -Uz select-word-style
select-word-style default
zstyle ':zle:*' word-chars '*?_-.[]~='

fpath=( ~/.home/zsh $fpath )
autoload -Uz tcsh-forward-word-match
zle -N forward-word tcsh-forward-word-match

以下を ~/.home/zsh/tcsh-forward-word-match に書きました。

# modified from /usr/share/zsh/4.3.12/functions/forward-word-match
emulate -L zsh
setopt extendedglob

autoload -Uz match-words-by-style

local curcontext=":zle:$WIDGET" word
local -a matched_words
integer count=${NUMERIC:-1}

if (( count < 0 )); then
    (( NUMERIC = -count ))
    zle ${WIDGET/forward/backward}
    return
fi

while (( count-- )); do

    match-words-by-style

    # For some reason forward-word doesn't work like the other word
    # commands; it skips whitespace only after any matched word
    # characters.

#    if [[ -n $matched_words[4] ]]; then
#        # just skip the whitespace
#	word=$matched_words[4]
#    else
#        # skip the word and trailing whitespace
#	word=$matched_words[5]$matched_words[6]
#    fi
    if [[ -n $matched_words[4] ]]; then
        # skip the whitespace and the word
	word=$matched_words[4]$matched_words[5]
    else
        # jist skip the word
	word=$matched_words[5]
    fi

    if [[ -n $word ]]; then
	(( CURSOR += ${#word} ))
    else
	return 1
    fi
done

return 0

M-f M-b の各シェルの動作

上記の設定の意味の解説をする前に、まずは tcsh, bash, zsh での動作を確認しておきます。(以下ではカーソル位置はスタイルシートの background-color で表現してありますので、RSS リーダーでご覧の場合には見えないかもしれません)

tcsh の場合 M-f を押すたびに次のようにカーソル(赤い四角)が移動します。

% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin 

逆に M-b を押すたびに次のようにカーソルが移動します。

% ls -l /usr/bin 
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin

M-f と M-b では停止位置が微妙に違うところがポイントです。

bash の場合 M-f の動作は tcsh と全く同じです。

$ ls -l /usr/bin
$ ls -l /usr/bin
$ ls -l /usr/bin
$ ls -l /usr/bin
$ ls -l /usr/bin 

しかし M-b の動作は -l の箇所の停止位置(青い四角)が tcsh とは異なります。

$ ls -l /usr/bin 
$ ls -l /usr/bin
$ ls -l /usr/bin
$ ls -l /usr/bin
$ ls -l /usr/bin

zsh を select-word-style bash で動作させた場合は、M-f の動作は次のようになります。

% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin 

bash の動作とは全く異なります。逆に M-b を押した場合は次のようになり bash の動作と一致します。

% ls -l /usr/bin 
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin

また、M-f での停止位置と M-b の停止位置が一致しています。

zsh を select-word-style default で動作させた場合は、M-f の動作は次のようになります。

% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin 

各引数の最初の文字に停止します。M-b を押した時も停止位置は同じです。

% ls -l /usr/bin 
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin

M-f M-b 解説

コマンドラインを構成する文字は単語を構成する文字(w とする)と単語を構成しない文字(. とする)の2種類にわけることができます。

すると tcshbash の場合のカーソルが停止する位置は、

  • M-f の場合はカーソルより右側の w. の境目 (www...)
  • M-b の場合はカーソルより左側の .w の境目 (...www)

となります。

zsh の場合は、

  • M-f の場合はカーソルより右側の .w の境目 (...www)
  • M-b の場合は tcsh, bash と同じ

となります。

そして単語を構成する文字は以下のようになっています。

tcsh [:alnum:] と '*?_-.[]~='
bash [:alnum:]*2
zsh で select-word-style bash [:alnum:]
zsh で select-word-style default [:alnum:] と '*?_-.[]~=/&;!#$%^(){}<>'

そこで、まずは zsh の単語を構成する文字を tcsh と一致させるために次のような設定をしました。

autoload -Uz select-word-style
select-word-style default
zstyle ':zle:*' word-chars '*?_-.[]~='

select-word-style default 時は word-chars に設定した文字に加えて [:alnum:] が単語を構成する文字になりますので、これで tcsh と一致します。

そして M-f の時の動作を変更するために次のような設定をし、

fpath=( ~/.home/zsh $fpath )
autoload -Uz tcsh-forward-word-match
zle -N forward-word tcsh-forward-word-match

~/.home/zsh/tcsh-forward-word-match を作成しました。tcsh-forward-word-match はカーソルより右側の w. の境目 (www...) へカーソルを移動させる関数です。

これでようやく tcsh とほぼ同じ感じで動作するようになりました。

*1:C Shell 系ではなく Bourne Shell 系のシェルを使いたいとずっと思っていたというのもあります

*2:未確認ですがおそらくこうなっています