「生年月日から年齢の簡単計算式」は、[これはひどい]か


[ネタ]タグがほとんどなく、ホントに実用コードとして感心してるっぽいブクマの数々。トリビア的ネタなら構わないけど、実用コードとして見ると、自分の最初の直感的感覚は[これはひどい]だった。

最近知ったんですが、生年月日から年齢を計算する簡単な計算式というのがあるそうです。

(今日の日付-誕生日)/10000の小数点以下切捨て。

生年月日から年齢を計算する簡単な計算式 | 日経 xTECH(クロステック)


自分がひどいと感じたその一番大きな理由は、ミョウチクリンな整数値で日付を表していること。日付なら日付の各種計算を扱えるデータ型があるべき、かつ、それを使うべき、という感覚だと思う。それと表裏一体の問題として、アルゴリズム(計算の意図)が明示されていないのが気持ち悪い。

後者の点から

y-((m-((d<birthD)? 1:0) <birthM)? 1:0) -birthY

なんかの方が全然マシ。


しかし同時に、そうは言っても、ちょっとハッキーでスマートかも、という風にも見えた。でも普通、年・月・日はどうせ持ってるし、逆にこんなヘンなint値を最初から持ってるとは考えにくいから、やっぱり全然スマートでもハッキーでもない。せめて

((y-birthY)*10000+(m-birthM)*100+(d-birthD))/10000

だよなぁ、と思い直すと(言語によってはさらに整数化が必要)、元ネタはやっぱり[ネタ]であって、実用コードとしては[これはひどい]だと思う。


そして、元エントリを読み直すと最後にある言葉、

アルゴリズムって芸術ですね。

生年月日から年齢を計算する簡単な計算式 | 日経 xTECH(クロステック)

だから、それアルゴリズムは表してないじゃん、って。というか、もともとプログラミングじゃなくてサーバ管理の連載で、箸休めネタだったんだろう。

以上、ネタにマジレスエントリ。

「OOは難しい」は真実であり、かつ嘘である。

http://alfalfa.livedoor.biz/archives/51079543.html


まとめがw。

しかし、OOを否定する手続き系マンセーな人達の一部は、コレを認識する必要があるかもしれない。


たしかにOOは設計情報が増える(≒難しい)が、それは、規模があって正しく整理された拡張性が高いプログラムを作る*1難しさが、他のカタチで表出したものに過ぎない。OOが無駄に複雑で難しいと言う手続き系マンセーの人には、単にこの難しさに対峙して来ていないだけ、という人が少なくないように思う。


当然、規模があって正しく整理された拡張性が高いプログラムを作るのは、別にOOである必要はないけど、それに使う道具としてOOは割とうまく行きやすいと言うことだろう。

*1:あえて、ここでは「書く」ではなく「作る」と言っておく

「私」という現象−自由意志なんてありませんが、何か?

著者は、なんと自由意志は存在しない、という立場に立っている。

これは怖い。実に怖い。なにしろ、怖がっている主体であるはずの「私」は存在しないというだけでも怖いのに、その怖さを避ける自由すら存在しない、と言っているに等しいのだから。

404 Blog Not Found:書評 - 脳の中の「私」はなぜ見つからないのか?

dankogaiのような天才エンジニアには、あまりにらしくない怖がり方に見える。


「私」が脳で生まれる以上、それはプログラムの実行体(仮にプロセスとする)に過ぎない。プロセスである以上、プログラムに沿った動作をするの当たり前で、すべては入力とプログラムによって決まる。しかし、それは「私」が存在しない事を意味するはずがない。現にプロセスとして存在している。

「私」の存在は、現時点においては「神」の存在以上に人類社会の根幹を成しているのだ。法律はその好例で、そこは自由意志の存在をあまりに当然のこととして仮定している。しかし決定論を認めてしまえば、実は法律は不要

404 Blog Not Found:書評 - 脳の中の「私」はなぜ見つからないのか?


そんな事はない。法学的には便宜的に自由意志という概念を使っているかもしれないが、自由意志がなくても高度な条件判断と自己書き換え機能を持つプログラムのプロセスに対して有効なのは当然であり、「バグった」プログラムで動くプロセスは、プログラムの矯正またはプロセス自体の排除がされるだけの事だ。


『いや、違う。もっと主体的な「私」、単なるプロセス以上の「私」が居ないのは認めたくない』と仰るかもしれない。それは、計算機との対比で重要なことを見落としているだけではないか。


それは、このプロセスは「感じる」という極めて特異な機能的性質を持つと言うことだ。


これは、計算機とプログラムで再現する方法は、知られていない。そしてこの特異な機能が、すべての「私」を「私」たらしめている物でもある。プログラムは、その「感じ方」すら自己書き換えを行う。プログラムと「私」を切り離して考えるなら、「私」が感じた事自体も、またプログラムの入力となる。この極めて不可思議な機能と高度なプログラムによって織りなされる現象が「私」。


『「私」の意志』と感じるのであれば、それは「私」にとっては「私の意志」に他ならない。入力とプログラムですべてが決まっているかどうかは、恐れる程の問題には思われない。学生の頃、そう考えた*1

*1:この「感じる」は、その後某氏がクオリアとして喧伝し始め、大いに期待した。しかし、その後の氏の売れ行きと実際的展開の落差に失望を禁じ得ないでいる

(失敗)ArrowでYコンビネータを…作れませんでした OTL

先日の"ArrowによるHaskellプログラミングの基礎。…パイプ感覚で順次/分岐/繰返し - よくわかりません"で、「あとで書く」にしていた「名前を使わない再帰」の方法を試してみたけど、結果は駄目だった。


ちゃんと理解してる人や素養のある人には自明なのかもしれないけど、アホな自分は元論文の"Programming with Arrows"を読んで、「Arrow*1一般での適用があるなら、それでYコンビネータ書けばArrowレベルで再帰できるじゃんw」と思って

y f = f (y f)

のまねして、関数適用をappに置き換えて

ya::forall a b aa.(ArrowApply aa)
    => aa (aa (aa a b) (aa a b)) (aa a b)
ya = arr (\ma -> (ma, (constA (ya,ma)>>>app))) >>> app
    where constA c = arr $ const c

こんなのを書いてみたけど駄目。

hoe.hs:2:51:
    Couldn't match expected type `aa a b'
	   against inferred type `b' (a rigid variable)
      `b' is bound by the type signature for `ya'
	at hoe.hs:0:13
      Expected type: aa (aa (aa a b) (aa a b), aa a (aa a b)) (aa a b)
      Inferred type: aa (aa (aa a b) (aa a b), aa a b) (aa a b)
    In the second argument of `(>>>)', namely `app'
    In the expression:
	  (arr (\ ma -> (ma, ( (constA (ya, ma)) >>> app)))) >>> app


constに無理があるよなあとか思いつつ、どうすりゃいいんだとウンウン考えてたら、

関数は適用すれば型が簡約(?)されるけど、Arrowは適用しても型が簡約(?)されない!?


という事に気がついた。だから、yaにmaを適用した結果がmaの型に合うはずがない。あれ?だめなのこれ?*2



…なんか、これも自分の勘違いのような気がしてきた…


追記:改めて見てみると、お話にならないな…。そもそも、Arrowは>>>してなんぼなのにappベースでどうするんだ。ダメだこりゃ(。∀ ゚)アヒャ!

*1:ArrowApplyの範囲で

*2:ひそかに理解と素養ある人のツッコミを期待

ArrowによるHaskellプログラミングの基礎。…パイプ感覚で順次/分岐/繰返し

Programming with Arrowsを読んで理解したつもりのメモ。誤りなど乞うご指摘。

(復習)Arrowってなに?

と思って以前調べたメモが"3分で解るHaskellのArrowの基本メモ - よくわかりません"。それにちょっと補足というか観点を変えてまず感覚の整理。


Monadに色んな種類があるように、Arrowも色んな種類がある。

  • Monad: IO、Maybe、…
  • Arrow: 関数そのまんま(->)、Kleisli m、…


ある種類のMonadに色んな型の色んな値を入れられるように、ある種類のArrowに色んな型の色んな関数を入れられる。

  • Monad: Maybeの例→ 「Maybe Int」 にreturn 0もreturn 777もOK。「Maybe Char」 にreturn 'a'もreturn ' 'もOK。
  • Arrow: (->)の例→ 「Int -> Int」 にarr (+1) もarr (*2)もOK。「Char -> Char」 にarr toUpperもarr toLowerもOK。

普通にパイプで繋ぐ … 順次処理構造

ふつうにArrowを「>>>」で繋ぐ。Arrowの種類が(->)つまり関数そのまんまの場合の例だと、

numOddOver10 :: (->) [Int] Int
numOddOver10 = (filter odd) >>> (filter (>10)) >>> length

のように3つのArrow(のインスタンスである(->))

  • (filter odd) :: (->) [Int] [Int]
  • (filter (>10)) :: (->) [Int] [Int]
  • length :: (->) [Int] Int

を、>>>で繋いで全体で(->) [Int] IntなArrow(のインスタンスである(->))を作れる。まさにUnixのシェルでコマンドをパイプ(|)で繋ぐ感覚。

使ってみるとこんな感じ。

Main> numOddOver10 [0,1,10,8,13]
1
Main> 

結果を、他の処理のあとで使う … 変数の代替

Unixのシェルでコマンドをパイプ(|)で繋げてて困るのが、出力を次のコマンドじゃなくてもっと後のコマンドに渡したい場合。その場合、一時ファイルのような外部記憶に一度吐いてまた読むという事になるけど、Arrowでは流れを論理上並列*1にできる。

それに便利なのがArrowのクラスメソッド(&&&)。

(&&&) :: arrow a b -> arrow a b' -> arrow a (b,b')

wikibooksにある概念図をみるとどんなものか解りやすい。


使用例。先の例の改変で、ただの奇数は1ポイント、10より大きい奇数はさらに10ポイントとしてカウントする。

numOddOver10Weighted :: (->) [Int] Int
numOddOver10Weighted = filter odd >>> 
                       ((filter(>10)>>>length>>>(*10)) &&& length) >>>
                       uncurry (+)


filter oddの出力が二股に分かれて、一方は&&&の左(filter(>10)>>>length>>>(*10))を通り、一方は&&&の右(length)を通る。これらの出力がタプルにまとめられてまた1本になってuncurry (+)に流れる。
まさにパイプ感覚というか流してる感覚。

並列系クラスメソッドは、ほかにfirst、second、(***)がある。それぞれどんなもんかはwikibooksの図を見れば解る。
ただ、流れが複雑になったらdo記法のArrow用拡張版で素直に変数を使った方が解りやすいかも。


値によって処理を分ける … 条件分岐構造

Unixのシェルでの、コマンドのパイプ連結だとお手上げ*2シェルスクリプトでif文の出番。

Arrowの場合も、Arrow一般だとお手上げだけど、ArrowChoiceというサブクラスの範囲では大丈夫。Arrowの枠内で条件分岐が出来る。(->)もKleisli mも、ArrowChoiceのインスタンスになってるから大丈夫。


ArrowChoiceのクラスメソッドがこれ。ふたつのArrowを渡して、条件によってそのどっちかだけを通すArrowを作ってくれる。

    (|||) :: arrow a c -> arrow b c -> arrow (Either a b) c

条件入力にはBoolの代わりにEitherを使う。イメージは上に書いた&&&に近い。Arrowふたつ( arrow a c と arrow b c )を取り、arrow (Either a b) cに合成する。合成されたArrowは、入力がLeft aなら arrow a cに流し、arrow b cは使われない。入力がRight bならその逆。

使用例。20より大きい要素がある場合は奇数である要素数、ない場合は10より大きい要素数をカウントする。

numOddOrOver10 :: (->) [Int] Int 
numOddOrOver10 =  hasOver20 >>> 
                  (filter(>10) ||| filter odd) >>>
                  length 
    where hasOver20 ns | any (>20) ns = Right ns 
                       | otherwise = Left ns

Rightの時は|||の右のArrowを通り、Leftの時は|||の左のArrowを通る。


ArrowChoiceのクラスメソッドは、ほかにleft、right、(+++)がある。それぞれどんなもんかはリファレンスとか参照。


一定条件の間は繰り返す … 繰り返し構造

forとかwhileです。もはやパイプでは有り得ませんが、Arrowでは最後に自分を持ってくればおk。

ra = ga>>>fa>>>ra

もちろんこれじゃ無限ループなので、

ra = ga>>>fa'>>>(ra|||arr id)

みたいに条件分岐と組み合わせる。


(追記:当初、ここにArrowApplyでループという記事を書きかけてたけど、自分のガセ妄想だったようです。すみません)

というわけで何でも出来る

上記の通り、変数の代替があって、順次処理構造、条件分岐構造、繰り返し構造が作れるので、どんなプログラムでも書けるはず。

       +
     +  +      / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
     ∧_∧    <  わーい構造化定理の3構造だー! 3構造だー
   br(´∀` )ワーイ !  |    何でもできるぞー
 +   ヽ    つ     \______________
      (⌒_ノ
       し'ゝ ;;::⌒::

かくして、純粋関数型言語Haskellの上に手続きプログラミングが華麗に再発明!



注意。実際はちゃんとarrしてArrowに

ここまで書いた例は(->)以外のArrowでは使えないコード。(->)以外のArrowでも使えるコードにするには、下のように関数をarrしてArrowにしてやらないとだめ。

numOddOver10 :: Arrow arrow => arrow [Int] Int
numOddOver10 = arr (filter odd) >>> arr (filter (>10)) >>> arr length
numOddOver10Weighted :: Arrow arrow => arrow [Int] Int
numOddOver10Weighted = arr (filter odd) >>> ((arr (filter(>10))>>>arr length>>>arr (*10)) &&& arr length) >>> arr (uncurry (+))
--こっちでも
--numOddOver10Weighted' = arr (filter odd) >>> (arr ((10*).length.(filter(>10))) &&& arr length) >>> arr (uncurry (+))
--numOddOver10Weighted'' = arr (filter odd) >>> (arr ((filter(>10))>>>length>>>(*10)) &&& arr length) >>> arr (uncurry (+))
numOddOrOver10 :: ArrowChoice arrow => arrow [Int] Int 
numOddOrOver10 =  arr hasOver20 >>> (arr (filter(>10)) ||| arr (filter odd)) >>> arr length 
    where hasOver20 ns | any (>20) ns = Right ns 
                       | otherwise = Left ns


断りなくあとで書き直すかも…。

追記:普通のArrowでは繰り返し構造が出来ないみたいに書いてたのを修正。

*1:実行が同時というわけではない

*2:もちろんコマンド自体が条件によって処理を変えるのは可能。それはArrowでもArrowの中の関数が条件によって処理を変えるのと同じ。ここではArrowのレベルでの条件分岐のルール(ツール)の話

イナゴ vs NHK+いのえもん

NHK「特報首都圏」ネットの“祭り”が暴走する by rina 政治/動画 - ニコニコ動画を観た。


お約束の印象操作的な手法とか、半ヤラセ丸出しの「当事者」素材とか、イナゴたちを釣り上げることにはそこそこ成功しているようだ。


自分は、地方ローカルの30分番組ではこんなもんだろう、と観ていたが、ひとつ気になるところがあった。


いのえもんの受け答えは、全体的には冷静で(前半の印象誘導を解消できたかは別として)大きくは的は外していないと思ったが、その一部がとくに引っ掛かった。

アナウンサ: マスコミの報道は既に偏向している、と、思っている、と言うことですか(薄ら笑いを浮かべながら)


いのえもん: まあ、そういう思い込みがあるんでしょうね。なにがしかマスコミは我々に隠していることがあるんではないかと


アナウンサ: マスコミには書いてない、実はこういう事なんだというのが、感情のフックになっていくということなんでしょうかね…(薄ら笑いを浮かべながら)


いのえもん: どんどんそこが引っ掛かっていくんでしょうね。それが次の新たな言説を生んでいって、だからこそマスコミは嘘を付いているんだ、という風に思ってしまって、「マスゴミ」と言ったりするということに繋がっていってるんだと思います


アナウンサ: マス「ゴミ」ですか(笑い調で)


いのえもん: はい(やや笑い調)


これって、基本的で重要な要因のひとつだと思うんだけど、笑い事にしてる場合なんだろうか。


この番組は、祭りと銘打ってはいるが、内容はそれに留まってはいない。とすれば、この機会にいのえもんがこれに切り込まなかった事は小さな問題ではない。