「ほっ」と。キャンペーン

<   2014年 02月 ( 10 )   > この月の画像一覧

積んどく本の処理のしかた。

机の上には読まないといけない本が積み上げてある。邪魔だが、本棚にしまってしまうとそのまま読まなくなってしまうのではないかとという気がするので、そのままにしているが邪魔なことこの上ない。

なんでそういうことになるのだろうと考えてみたが、要するに本を読むのは結構たいへんだからだ。脳もフル回転しないといけない。おまけに、必要な情報を理解できないかもしれないという鬱陶しさもある。それに読んでない本は文字の羅列で、ページに色がついていない。

こういうときは目次を読むことを勧められているが、基礎知識がほとんどない分野の目次を読んでも内容の予測をできることはあまりない。目次の項目の関連性や重要度が読めないからだ。

そこで、目次や見出しに色をつけてみることを考えてみた。それぞれの項目の横に次のような文字を書き込むようにしたのだ。
  1. A は内容が則、実務に役立つ場合。
  2. C は実務には利用しないが知識として知っておきたい概念
  3. P は専門的に必要になる細かい記述
  4. T は使わないであろう知識

例えば薬の添付文書であれば、A は適応症や用法用量、P は他の薬剤との相互作用や、血中濃度の半減期、C は薬剤の化学構造や作用部位などになるだろう。

分け方は各人の好みでいいと思うが、知識を素早く役立てたい場合に、項目を見てざっとその性格を思い出すための工夫だ。

T は trash can からとったのだが、本を全て読んで理解するのは時間的にも能力的にも無理なのに、本があるとどうしても全てを読みたくなってしまう強迫観念を捨てるための記号だ。つらいことだが、無理なことはやはり無理だ。

実際に数年積んどくしていた参考書に使ってみたが、見出しに上のようなタグ付けをしたところ、自分が何を読もうとしているのかがよくわかり、無事に本棚行きにすることができた。

実際に使えそうな知識には A をつけて精読し、細かいテクニックは P をつけてざっと流すだけにした。また、理論的なことは C にして、A の項目と関連の深そうなところだけを真剣に読んだ。また、重要な項目は付箋を貼ってコメントをつけておいた。これはこの本を読んだということが本を眺めただけでわかるので便利だった。

よく考えたら他の人は既にやっている工夫だった。今まで気が付かなかったのはそれだけ勉強をサボっていたのを反映している。
[PR]
by tnomura9 | 2014-02-25 12:58 | 考えるということ | Comments(0)

実数直線上には一点は存在しない。

アキレスと亀のパラドックスや、飛ぶ矢のパラドックスなどゼノンのパラドックスはどちらも連続性の矛盾に関連するものだ。連続性の矛盾というと専門家に叱られるかもしれないが、連続性の定義から推論する限り連続体には一点というものは存在しないという結論になる。

抽象的になるのを避けるために実数直線について考えてみよう。実数直線上では異なる2点の間には必ず2点と異なる別の点を見つけることができる。

実数直線上で他の点との重複のない一点があると仮定する。それを仮に点 p とする。点 p は唯一であるということは点 p が他の点とは重複が全くないということである。すなわち点 p は実数直線上のどの点とも重ならない。

ここで、実数直線上で点 p とは異なる全ての点を集めた集合を Q とする。そうすると集合 Q の要素である実数上の点は点 p との距離によって分類することができる。そこで、そのうち点 p に最も近い点を q とする。しかし点 q は点 p とは異なっている。そうすると実数直線の連続性から点 p と点 q の間に点 r が存在するはずだ。しかし、これは点 r が点 q より点 p に近くなり集合 Q には含まれない。これは集合 Q が点 p 以外の全ての点の集合であるという過程に反する。したがって、結論が矛盾しているので、実数直線上に一点が存在するという仮定が誤りであることが証明される。

これには結論の矛盾がしめすのは点 p とは異なる全ての点の集合には点 p に最も近い点が存在しないということを意味するだけで、点 p が存在しないということを意味するのではないという反論があるかもしれない。デデキントの切断だ。しかしそうなると集合 Q は点 p に限りなく近づく点を要素として含んだ無限集合ということになるのだろうか。

点 p が存在しないという立場と、点 p に無限に近づく点を要素として持つ無限集合 Q が存在するという立場は、実数直線の性質を別の眺め方で見ているにすぎないように思える。

単純に実数直線上には点はなく、無限に縮小する区間で点という概念を表すのだと考えたほうが、ゼノンのパラドックスに対する反論を作りやすいのではないだろうか。

アキレスと亀のパラドックスで言うとアキレスと亀の観測時点は点ではなく微小な区間であるから、アキレスが亀に追いつく可能性を排除できないと反論できるし、飛ぶ矢のパラドックスでは、瞬間という点は存在せず無限小の区間であると考えるべきだと反論できる。

もちろんそれでも連続とは何かという疑問は残ってしまうが。

追記

この議論のポイントは、「隣り合う2点の間に必ず点が存在する」という連続性の定義が、一点を他の点と区別することを不可能にしているということだ。実数に埋め込まれた整数は一点だというかも知れないが、たとえば 1 という整数でも実数直線上にあるときには実数直線上の 1 の近傍の点と区別することは不可能になる。1 という点が実数上の他の点とは全く重ならないユニークな点であるということを主張できなくなってしまうからだ。

円周率の小数点表示の最終的な値が永遠に求められないように、1 と 1.000... と 0.999... の差も永遠に求めることはできない。

こういう意味でも実数に整数の一点という考え方を簡単には当てはめられないことが分かる。実数における一点については本質的に不確実性を排除できないからだ。
[PR]
by tnomura9 | 2014-02-18 19:02 | 考えるということ | Comments(2)

smart constructors その5

前へ 目次 次へ

参照: cabal-instsll 関連のリンク Distribution.Simple.Commnad Distribution.Simple.Setup

引き続き、Distribution.Simple.Command の OptDescr smart constructors セクションを見てみる。最初に今回の記事を ghci で試すための準備をする。

Prelude> :set prompt "ghci> "
ghci> import Distribution.Simple.Command
ghci> import Distribution.Simple.Setup
ghci> import Distribution.ReadE
ghci> import Data.Monoid

boolOpt

boolOpt のソースコードは次のようになる。

boolOpt :: (b -> Maybe Bool) -> (Bool -> b) -> SFlags -> SFlags -> MkOptDescr (a -> b) (b -> a -> a) a
boolOpt g s sfT sfF _sf _lf@(n:_) d get set =
    BoolOpt d (sfT, ["enable-"++n]) (sfF, ["disable-"++n]) (set.s) (g.get)
boolOpt _ _ _ _ _ _ _ _ _ = error "Distribution.Simple.Setup.boolOpt: unreachable"

また、BoolOpt コンストラクタ型の構成は次のようになる。

ghci> :info BoolOpt
data OptDescr a
  = ...
  | BoolOpt Description
            OptFlags
            OptFlags
            (Bool -> a -> a)
            (a -> Maybe Bool)
        -- Defined in `Distribution.Simple.Command'

ghci> :info OptFlags
type OptFlags = (SFlags, LFlags)
        -- Defined in `Distribution.Simple.Command'

しかし、boolOpt の変数や型を見ても具体的イメージがわかない。さいわい Distribution.Simple.Setup の installCommand には BoolOpt コンストラクタ型の OptDescr 型のデータが含まれている。installCommand の BoolOpt コンストラクタ型のデータを作成するソースコードは次のようになる。

      ,option "" ["shell-wrappers"]
         "using shell script wrappers around executables"
         installUseWrapper (\v flags -> flags { installUseWrapper = v })
         (boolOpt [] [])

これを見ると boolOpt 関数の get 変数には installUseWrapper が、set 変数には (\v flags -> flags { installUseWrapper = v }) 関数が割り当てられることがわかる。また、(boolOpt [] []) が option 関数に渡されているが、しかし、これは Distribution.Simple.Command に定義されている boolOpt とは引数の型が異なっている。

不思議に思って boolOpt [] [] の型を調べてみたら次のようなエラーメッセージがでた。

ghci> :t boolOpt [] []

:1:1:
    Ambiguous occurrence `boolOpt'
    It could refer to either `Distribution.Simple.Setup.boolOpt',
                             imported from `Distribution.Simple.Setup'
                          or `Distribution.Simple.Command.boolOpt',
                             imported from `Distribution.Simple.Command'

どうやら Distribution.Simple.Setup と Distribution.Simple.Command の両方に boolOpt 関数が定義されているらしい。そこで Distribution.Simple.Setup のソースコードを探してみたら boolOpt 関数の次のような定義が見つかった。

boolOpt :: SFlags -> SFlags -> MkOptDescr (a -> Flag Bool) (Flag Bool -> a -> a) a
boolOpt = Command.boolOpt flagToMaybe Flag

これを見ると boolOpt は Distribution.Simple.Setup モジュールで Distribution.Simple.Command.boolOpt 関数を使ってオーバーライドされていたようだ。

これをみると Command.boolOpt の引数 g に flagToMaybe が、s に Flag が、sfT に [] が、sfF に [] が、_sf に "" が、_lf に ["shell-wrappers"] が、d に "using shell script wrappers around executables" が、get に installUseWrapper が、set に (\v flags -> flags { installUseWrapper = v })が割り当てられて Command.boolOpt の全ての変数が充足されていることがわかる。

そこで Command.boolOpt を使ってマニュアルで BoolOpt コンストラクタ型の OptDescr 型データを作成して foo にバインドした。

ghci> let foo = Distribution.Simple.Command.boolOpt flagToMaybe Flag [] [] "" ["shell-wrappers"] "using shell script wrappers around executables" installUseWrapper (\v flags -> flags {installUseWrapper = v})

ghci> :t foo
foo :: OptDescr InstallFlags

パターンマッチで BoolOpt コンストラクタのパラメータを抽出して機能を調べてみた。

ghci> :info BoolOpt
data OptDescr a
  = ...
  | BoolOpt Description
            OptFlags
            OptFlags
            (Bool -> a -> a)
            (a -> Maybe Bool)
        -- Defined in `Distribution.Simple.Command'
ghci> let (BoolOpt d oft off set get) = foo
ghci> d
"using shell script wrappers around executables"
ghci> oft
("",["enable-shell-wrappers"])
ghci> off
("",["disable-shell-wrappers"])
ghci> get defaultInstallFlags
Just False
ghci> get $ set True defaultInstallFlags
Just True

boolOpt'

boolOpt' は boolOpt の亜種のようだが、定義のソースコードは次のようになる。

boolOpt' :: (b -> Maybe Bool) -> (Bool -> b) -> OptFlags -> OptFlags -> MkOptDescr (a -> b) (b -> a -> a) a
boolOpt' g s ffT ffF _sf _lf d get set = BoolOpt d ffT ffF (set.s) (g . get)

この定義で boolOpt と違うところは _sf の型が SFlags ではなく、OptFlags になっていること、_lf の型がやはり SFlags ではなく、OptFlags になっていることだ。Sfrags と OptFlags の型はそれぞれ次のようになっている。

ghci> :info SFlags
type SFlags = [Char]    -- Defined in `Distribution.Simple.Command'
ghci> ghci> :info SFlags

ghci> :info OptFlags
type OptFlags = (Distribution.Simple.Command.SFlags, LFlags)
        -- Defined in `Distribution.Simple.Command'

boolOpt' の使い方も実例を探さなければならないが、疲れたので今回は調べなかった。
[PR]
by tnomura9 | 2014-02-11 17:58 | Haskell | Comments(0)

IT断食

「やっぱりな」という記事を見かけたので引用しておく。(元記事

「AERA」2月3日号では、ソフトウェア企業のドリーム・アーツが実施している「IT断食」を紹介している。この会社では、会議へのパソコン・スマホの持ち込みや、プレゼンソフトのパワーポイントの使用、CCメール(写しメール)の送信を禁止している。

2013年には社内の猛反対を押して、営業部からパソコンを取り上げ、企画書や見積書作成などの業務は、別の支援部門が担当することにした。

同社のウェブサイトによると、現在営業部はパソコンの代わりにタブレットPC(iPad)を持って外回りをしている。パソコンよりも素早く電源を立ち上げることができるので、移動中にメールチェックをすることもできる。

さらに、資料をクラウド上に保存しておけば、訪問先でもパソコンを使う必要がない、というわけだ。パソコン撤去後の半年間で、営業部の訪問件数は40件から250件に増加。売上見込みも3倍以上になる成果が出たという。


あえてITを使わないというのも仕事の合理化のひとつかもしれない。製品を売るという目的を考えたときには常識と考えられていることも疑う必要がある事を示している。それと、製品を売る事の大切さを。

ブラジルのセムラー社では販売部門と製造部門の人員がチームを作っており、意思決定は顔と顔を合わせて行うミーティングで行われる。その際、口頭ですむ用事は極力文書を作らないようにしているとのことだ。情報を文書化するときの文書化できにくい情報が抜け落ちたり、文書を作ったり読んだりする無駄な時間が発生したりするのを防ぐためだ。

現場で発生したクレームはすぐに製造部門に届くので素早い対処ができる。これも、ユーザーの意見に的確な対応をするという製品を売るための最適化のひとつだろう。

ソニーのパソコンを例に挙げて悪いが、カーソルキーが右のシフトキーに並んでいたため打ち間違いが多かった。これについての不満が多くネットの記事にも見られていたのに、何年も放置されていた。売れない理由は価格のせいだけではない。

販売力の強化などとスローガンを掲げるより、製品を売るためには本当に何をしないといけないのかを真剣に考える必要がある。
[PR]
by tnomura9 | 2014-02-11 04:02 | 話のネタ | Comments(0)

smart constructors その4

前へ 目次 次へ

参照: cabal-instsll 関連のリンク Distribution.Simple.Commnad Distribution.Simple.Setup

引き続き、Distribution.Simple.Command の OptDescr smart constructors セクションを見てみる。最初に今回の記事を ghci で試すための準備をする。

Prelude> :set prompt "ghci> "
ghci> import Distribution.Simple.Command
ghci> import Distribution.Simple.Setup
ghci> import Distribution.ReadE
ghci> import Data.Monoid

noArg

noArg のソースコードは次のようになる。

noArg :: (Eq b, Monoid b) => b -> MkOptDescr (a -> b) (b -> a -> a) a
noArg flag sf lf d = choiceOpt [(flag, (sf,lf), d)] sf lf d

noArg 関数は内部で choiceOpt 関数を使っている。choiceOpt 関数の定義は次のようになっている。フラグと OptFlags (ショートフラグのリストとロングフラグのリストのペア) とオプションの説明文からなるタプルのリストを引数とし、MkOptDescr 型の関数を返す。

-- | create a Choice option
choiceOpt :: Eq b => [(b,OptFlags,Description)] -> MkOptDescr (a -> b) (b -> a -> a) a
choiceOpt aa_ff _sf _lf _d get set  = ChoiceOpt alts
    where alts = [(d,flags, set alt, (==alt) . get) | (alt,flags,d) <- aa_ff]

choiceOpt の引数の (flag, (sf,lf), d) というタプルにはフラグ(flag)、ショートオプションのリスト(sf)とロングオプションのリスト(lf)のペア、それに、オプションの説明(d) が収められている。さらに choiceOpt 関数はこのタプル単体ではなくそのリストを引数にしている。

choiceOpt 関数はこの (flag, (sf,lf), d) のタプルから (d, flags, set alt, (==alt) . get) というタプルを作り出す。次にしめす ChoiceOpt コンストラクタの型を見ると、d は オプションの説明、flags は (sf,lf) のペア, set alt はフラグに値を設定する関数、(==alt) . get はフラグの内容を表示する関数であることが分かる。

ghci> :t ChoiceOpt
ChoiceOpt
  :: [(Description, OptFlags, a -> a, a -> Bool)] -> OptDescr a

ChoiceOpt について解読するためには実例を使うのが一番良い。この記事のシリーズの以前の記事で Distribution.Simple.Setup の globalCommand には ChoiceOpt の実例が含まれているのが分かっている。また、その取り出し方は「CommnadUI の読み方」の記事で調べたが、ここで復習してみる。ghci に冒頭で述べた処理をしておくと以下のコードを実行することができる。

Distribution.Simple.Setup の globalCommand のすべての情報は CommandUI 型の globalCommand に保存されている。ChoiceOpt は globalCommand の commandOptions フィールドの中に収められている。まず commandOptions フィールドのデータの型を見てみる。

ghci> :t commandOptions globalCommand
commandOptions globalCommand
  :: ShowOrParseArgs -> [OptionField GlobalFlags]

これは commandOptions フィールドの値が ShowOrParseArgs の値で切り替えられる OptionField のリストであることが分かる。そこで ShowArgs の場合の値の型を見てみる。データが Show クラスのインスタンスではない場合 ghci で内容の表示ができないが、:t コマンドを使うとデータの型は見ることができる。

ghci> :t commandOptions globalCommand $ ShowArgs
commandOptions globalCommand $ ShowArgs
  :: [OptionField GlobalFlags]

OptionField 型のリストを取り出せた。OptionField 型の構成は次のようになっている。

data OptionField a
  = OptionField {optionName :: Name, optionDescr :: [OptDescr a]}
      -- Defined in Distribution.Simple.Command'

OptionField のリストにどんなオプションが入っているか optionName で調べてみた。

ghci> map optionName $ commandOptions globalCommand $ ShowArgs
["version","numeric-version"]

そこで "version" オプションの optionDescr を取り出し、foo にバインドした。

ghci> let foo = optionDescr $ head $ commandOptions globalCommand $ ShowArgs
ghci> :t foo
foo :: [OptDescr GlobalFlags]

foo には何個の要素が含まれているか検査してみた。

ghci> length foo
1

どうやら1個しかないようなのでそれを取り出して、bar にバインドした。

ghci> let bar = head foo
ghci> :t bar
bar :: OptDescr GlobalFlags

これでついに ChoiceOptの実例を取り出せた。

しかし、これで終わりではない。ChoiceOpt のパラメータは次のように (Description, OptFlags, a -> a, a -> Bool) タプルのリストになっているからだ。

ghci> :t ChoiceOpt
ChoiceOpt
  :: [(Description, OptFlags, a -> a, a -> Bool)] -> OptDescr a

そこでパターンマッチで bar のパラメータを取り出し baz にバインドした。

ghci> let baz = p where (ChoiceOpt p) = bar
ghci> :t baz
baz
  :: [(Description,
      OptFlags,
      GlobalFlags -> GlobalFlags,
      GlobalFlags -> Bool)]

baz の要素数を調べてみると 1 だったので、要素のタプルは1個だけだ。

ghci> length baz
1

そこで bas の1個だけのタプルから、タプルの要素 d, flags, set, get をとりだした。

ghci> let (d, flags, set, get) = head baz
ghci> d
"Print version information"
ghci> flags
("V",["version"])
ghci> :t set
set :: GlobalFlags -> GlobalFlags
ghci> :t get
get :: GlobalFlags -> Bool

get を試してみた。

ghci> get defaultGlobalFlags
False

これは defaultGlobalFlags のパラメータの最初のフラグの値を取り出している。

ghci> :t GlobalFlags
GlobalFlags :: Flag Bool -> Flag Bool -> GlobalFlags

set も試してみた。

ghci> get $ set defaultGlobalFlags
True

set 関数で Flag True にセットしたフラグの値を get で確認している。

ChoiceOpt [(d, flags, set, get)] の説明文の d や、ショートオプションとロングオプションのペア flags :: (sf,lf) については特に問題ないので、set や get の関数を定義できればマニュアルで ChoiceOpt 型のデータが作成できる。

まず get を考える。GlobalFlags の構成は次のようになうから、

ghci> :info GlobalFlags
data GlobalFlags
  = GlobalFlags {globalVersion :: Flag Bool,
        globalNumericVersion :: Flag Bool}
      -- Defined in `Distribution.Simple.Setup'
instance Monoid GlobalFlags
  -- Defined in `Distribution.Simple.Setup'

globalVersion アクセサで defaultGlobalFlags がら globalVersion フィールドのフラグ値が取り出せる。

ghci> globalVersion defaultGlobalFlags
Flag False

しかしながら、get 関数では Flag 型ではなく Bool 型が戻ってきて欲しい。そこで (== Flag True) を使うとうまい具合に上の例でも False が戻ってくる。

ghci> (== Flag True) $ globalVersion defaultGlobalFlags
False

つぎに set 関数の場合だが、globalVersion フィールド名を使うとフラグの値をセットできる。

ghci> globalVersion $ defaultGlobalFlags {globalVersion = Flag True}
Flag True

注: デフォールトのフラグの値も利用しつつ mappend で新しい値と演算したあとでフラグをセットしたい場合もある。元々の値に新しい値をアペンドしたいときなどだ。直接に記述するとコードが長くなりすぎるので、つぎのような mget と mset を定義する。

ghci> let mget = globalVersion
ghci> let mset v = defaultGlobalFlags {globalVersion = v}

この mget, mset を利用してフラグの元の値と新しい値のアペンドを行ってフラグの値を再設定する map 関数を次のように定義することができる。

ghci> let map w = mset $ mget defaultGlobalFlags `mappend` w
ghci> mget $ map (Flag True)
Flag True

上の結果ではフラグの値が単に新しい値に置き換わっただけだが、Flag a の mappend がそういう定義になっているためだ。

ghci> Flag False `mappend` Flag True
Flag True

choiceOpt 関数の定義を再掲するが、上の仕組みを頭において眺めるとコードの意味が分かる。

-- | create a Choice option
choiceOpt :: Eq b => [(b,OptFlags,Description)] -> MkOptDescr (a -> b) (b -> a -> a) a
choiceOpt aa_ff _sf _lf _d get set  = ChoiceOpt alts
    where alts = [(d,flags, set alt, (==alt) . get) | (alt,flags,d) <- aa_ff]

ちなみに globalCommand の version オプションの場合、

d = "Print version information"
flags = (['V'], ["version"])
alt = Flag True
set = (\v flags -> flags { globalVersion = v })
get = globalVersion

のようになる。これらは、globalCommnad の定義をする際の option 関数の引数として使われている。Distribution.Simple.Setup の globalCommand の定義の該当部分を再掲すると次のようになる。

    commandOptions = \_ ->
      [option ['V'] ["version"]
        "Print version information"
        globalVersion (\v flags -> flags { globalVersion = v })
        trueArg
      ,option [] ["numeric-version"]
        "Print just the version number"
        globalNumericVersion (\v flags -> flags {globalNumericVersion = v })
        trueArg
      ]

最後に choiceOpt 関数を使って ChoiceOpt コンストラクタ型のデータをマニュアルで作り、この記事を終わる。

ghci> let hoge = choiceOpt [(Flag True, (['V'],["version"]), "Print version information")] ['V'] ["version"] "Pring version information" globalVersion (\v flags -> flags {globalVersion = v})

ghci> :t hoge
hoge :: OptDescr GlobalFlags

ghci> let (_, _, hset, hget) = head h where (ChoiceOpt h) = hoge
ghci> hget $ hset defaultGlobalFlags
True
[PR]
by tnomura9 | 2014-02-09 17:24 | Haskell | Comments(0)

smart constructors その3

前へ 目次 次へ

参照: cabal-instsll 関連のリンク Distribution.Simple.Commnad Distribution.Simple.Setup

引き続き、Distribution.Simple.Command の OptDescr smart constructors セクションを見てみる。最初に今回の記事を ghci で試すための準備をする。

Prelude> :set prompt "ghci> "
ghci> import Distribution.Simple.Command
ghci> import Distribution.Simple.Setup
ghci> import Distribution.ReadE
ghci> import Data.Monoid

optArg

optArg の型は次のようになる。reqArg の型と似ているが第3引数に余計な b 型の引数がある。これにはデフォールトのフラグ値を指定する事ができる。

optArg
  :: Monoid b =>
     ArgPlaceHolder
     -> ReadE b
     -> b
     -> (b -> [Maybe String])
     -> MkOptDescr (a -> b) (b -> a -> a) a

optArg のソースは次のようになる。

-- | Create a string-valued command line interface with a default value.
optArg :: Monoid b => ArgPlaceHolder -> ReadE b -> b -> (b -> [Maybe String])
                   -> MkOptDescr (a -> b) (b -> a -> a) a
optArg ad mkflag def showflag sf lf d get set  =
  OptArg d (sf,lf) ad (fmap (\a b -> set (get b `mappend` a) b) mkflag)
               (\b ->          set (get b `mappend` def) b)
               (showflag . get)

optArg 関数は単に OptArg コンストラクタのパラメータを設定するだけだ。このうち

(\b ->          set (get b `mappend` def) b)

以外は reqArg 関数と共通なので意味が分かる。上のコードは reqArg の類推から、フラグセット b の値を取り出して def (デフォールトのフラグ値)と b のフラグとの mappend 操作を行い、その値を再びフラグセット b の値として再設定するという意味があることが分かる。

手っ取り早く言うとフラグセットを引数にとり、そのなかの特定のフラグの値をデフォールト値にするという意味がある。

例によって myget, myset, mydef を定義して実験してみる。

ghci> let myget = installDistPref
ghci> let myset = (\d flags -> flags {installDistPref = d})
ghci> let mydef = Flag "foo"

この条件の下で

(\b ->          set (get b `mappend` def) b)

を試してみる。

ghci> let f = \b -> myset (myget b `mappend` mydef) b
ghci> f defaultInstallFlags
InstallFlags {installPackageDB = NoFlag, installDistPref = Flag "foo", installUseWrapper = Flag False, installInPlace = Flag False, installVerbosity = Flag Normal}

したがって、optArg 関数によって作られる OptArg コンストラクタ型のデータはそのパラメータの中に、デフォールトのフラグ値をセットする関数が含まれていることが分かる。

optArg'

optArg' の型は次のようになる。

ghci> :t optArg'
optArg'
  :: Monoid b =>
     ArgPlaceHolder
     -> (Maybe String -> b)
     -> (b -> [Maybe String])
     -> MkOptDescr (a -> b) (b -> a -> a) a

optArg と違うのは mkflag の型が Mabe String -> b になっているのと、def (デフォールト値) がないことだ。しかし次に示す optArg' のソースを見ると、optArg' は optArg の String -> a 型変種 ということになっている。

-- | (String -> a) variant of "optArg"
optArg' :: Monoid b => ArgPlaceHolder -> (Maybe String -> b) -> (b -> [Maybe String])
                    -> MkOptDescr (a -> b) (b -> a -> a) a
optArg' ad mkflag showflag =
    optArg ad (succeedReadE (mkflag . Just)) def showflag
      where def = mkflag Nothing

このソースでは optArg' も reqArg' と同じように内部で optArg を使っている。つまり、optArg の mkflag の部分が (mkflag . Just) に変わっている。def の方は where 句で def = mkflag Nothing に設定してある。

mappend

reqArg の記事では触れなかったが、reqArg と optArg のソースの解読では謎が残っている。それは ReqArg の パラメータのうちでフラグをセットするパラメータを作る、

(fmap (\a b -> set (get b `mappend` a) b) mkflag)

の部分だ。このコードで `maapend` が何をしているのかという事である。mappend は Monoid クラスの多相関数で Monoid クラスのインスタンスのデータ同士の演算を行う。たとえば Flag 型のデータも Monoid クラスのインスタンスなので `mappend` で演算ができ、つぎのような結果になる。

ghci> Flag "foo" `mappend` Flag "bar"
Flag "bar"

つまり、最初の Flag "foo" が後の Flag "bar" で置き換えられている。しかし、リストの場合は次のように mappend はデータを繋いでいく。

ghci> [Flag "foo"] `mappend` [Flag "bar"]
[Flag "foo",Flag "bar"]

おそらく reqArg や optArg のコードの mappend も上のリストの場合のような動作も想定してあるのだろうが、実際にはどういう使い方をされているのかはもう少し全体のコードを解読しないとわからない。
[PR]
by tnomura9 | 2014-02-09 07:49 | Haskell | Comments(0)

smart constructors その2

前へ 目次 次へ

参照: cabal-instsll 関連のリンク Distribution.Simple.Commnad Distribution.Simple.Setup

引き続き、Distribution.Simple.Command の OptDescr smart constructors セクションを見てみる。最初に今回の記事を ghci で試すための準備をする。

Prelude> :set prompt "ghci> "
ghci> import Distribution.Simple.Command
ghci> import Distribution.Simple.Setup
ghci> import Distribution.ReadE
ghci> import Data.Monoid

reqArg'

reqArg' は reqArg と同じように ReqArg コンストラクタ型のデータを作る MkOptDescr 型の関数を作成するが reqArg とは少し違う所がある。reqArg' の型は次のようになる。

ghci> :t reqArg'
reqArg'
  :: Data.Monoid.Monoid b =>
     ArgPlaceHolder
     -> (String -> b)
     -> (b -> [String])
     -> MkOptDescr (a -> b) (b -> a -> a) a

reqArg' の引数の型は第2引数の mkflag の型が (String -> b) になっている。reqArg の場合はこれが、ReadE b だった。reqArg の mkflag は ReadE 型のパーサーだったが、reqArg' は String -> b 型の関数だ。

reqArg' のソースは次のようになっている。

-- | (String -> a) variant of "reqArg"
reqArg' :: Monoid b => ArgPlaceHolder -> (String -> b) -> (b -> [String])
                    -> MkOptDescr (a -> b) (b -> a -> a) a
reqArg' ad mkflag showflag =
    reqArg ad (succeedReadE mkflag) showflag

これをみると分かるように reqArg' は内部的に reqArg 関数を利用している。そこで、reqArg 関数の定義をみると次のようになっている。reqArg に mkflag ではなく、succeedReadE mkflag が渡されている。

reqArg :: Monoid b => ArgPlaceHolder -> ReadE b -> (b -> [String])
                   -> MkOptDescr (a -> b) (b -> a -> a) a
reqArg ad mkflag showflag sf lf d get set =
  ReqArg d (sf,lf) ad (fmap (\a b -> set (get b `mappend` a) b) mkflag) (showflag . get)

したがって、reqArg' の値が reqArg の値と異なっているのは ReqArg コンストラクタの第4パラメータの部分が、

(fmap (\a b -> set (get b `mappend` a) b) (succeedReadE mkflag))

となる所だけだ。

fmap (\a b -> set (get b `mappend` a) b)

というコードは succeedReadE 型のパーサのなかのフラグセットのひとつのフラグについて、既存の値を取り出してそれを新しい値と置き換えまたはマージして(mappend で行う) それを再びフラグの値としてセットするという動作をする。

これをテストするために前回の記事と同じように、myset, myget, mymkflag を定義する。

ghci> let myget = installDistPref
ghci> let myset = (\d flags -> flags {installDistPref = d})
ghci> let mymkflag = (\str -> Flag str)

最初に succeedReadE mymkflag の型を調べてみる。

ghci> :t succeedReadE mymkflag
succeedReadE mymkflag :: ReadE (Flag String)

これは ReadE 型のパーサになるから、runReadE で文字列をパースしてみる。

ghci> runReadE (succeedReadE mymkflag) "foo"
Right (Flag "foo")

これは前回の記事の

ghci> runReadE mymkflag "bar"
Right (Flag "bar")

の場合と結果は同じだ。違うのは mkflag が明示的に ReadE 型のパーサになっているのではなく、 suceedReadE mymkflag のようにreqArg' の内部で mkflag :: String -> b 型の関数から ReadE 型のパーサを作り出している所だ。

したがって reqArg' で作った ReqArg コンストラクタ型のデータも前回の reqArg の場合と同じような動作になる。

ghci> let (Right f) = runReadE (fmap (\a b -> myset (myget b `mappend` a) b) (succeedReadE mymkflag)) "foo"
ghci> f defaultInstallFlags
InstallFlags {installPackageDB = NoFlag, installDistPref = Flag "foo", installUseWrapper = Flag False, installInPlace = Flag False, installVerbosity = Flag Normal}

まとめると reqArg' の動作は reqArg と本質的には一緒だ。しかし、reqArg 型が mkflag に ReadE 型のパーサを当てるのに対し、reqArg' は mkflag に Strig -> b 型の関数を使うという事だけだ。reqArg 関数はどのようなパーサも利用できるが、reqArg' は主に文字列をフラグに変換するという関数しか使えない。このように reqArg' 関数の場合用途は限られてくるが、パーサを自前で作るという手間が省ける。

Distribution.Simple.Command のコードは、データにパーサを使ったり fmap を活用したりなど抽象的なプログラミングがあり、最初は取り付きにくいが、ghci で動作確認して追跡してみると、このような抽象的なプログラミングが、意外にアルゴリズムとしてのプログラムをそのまま表現してくれることがわかる。

たとえば fmap に対し、コンストラクタのパラメータをコンストラクタに入れたまま操作するのだというイメージでとらえると途端にわかりやすくなる。また、ReadE 型のパーサにしても runReadE アクセサに パーサと文字列を与えると、Either 型のパラメータに入れられた関数が取り出せるというパターンに慣れると悩まなくても操作できるようになる。

抽象的なプログラムは論理で考えるのではなく、ghci を操作してその動作をパターン (関数やデータの型) でとらえると、それが、手続き型のプログラムに比べ思考のパターンに親和性があることが感じられる。
[PR]
by tnomura9 | 2014-02-07 23:24 | Haskell | Comments(0)

やっこ まなこ ぺんた みこ まりやん ピンキー りりやん さつき

【やこまな】スイートデコラアイスクリームホリック【オリジナル振り付け】

【やこまなぺんた】スノートリック 踊ってみた【あいしてる】

【みこ☆ぺん】I❤ 踊ってみた【みことぺんた】

【やっこ×まりやん】cLick cRack 踊ってみた【オリジナル振付】

【まりやん&ピンキー!】 トゥインクル を踊ってみた 【やんキー!】

【念願のお嬢様と一緒に】ぴんこすてぃっくLuv 【踊ってみた】

【やこまなさつき】妄想税 踊ってみた
[PR]
by tnomura9 | 2014-02-05 06:11 | ボーカロイド | Comments(0)

smart constructors

前へ 目次 次へ

参照: cabal-instsll 関連のリンク Distribution.Simple.Commnad Distribution.Simple.Setup

Distribution.Simple.Command の OptDescr smart constructors セクションを見てみる。

このセクションには前回述べた OptDescr 型のデータを作成するための関数 smart constructors が収められている。

最初に MkOptDescr get set a が記述されているが、これは type 宣言されているので単なる型の別名だ。また、これは特定の機能を持つ関数の型を表している。

前回述べたように OptDescr 型のコンストラクタは ReqArg, OptArg, ChoiceOpt, BoolOpt の4つがありそれぞれパラメータの構成が異なっている。これらの異なるコンストラクタのデータを、1つの option 関数で作成するために、それぞれのコンストラクタに対応する MkOptDescr get set a 型の関数を option 関数の引数として渡すように設計してある。

MkOptDescr の type 宣言は次のようになる。

type MkOptDescr get set a = SFlags -> LFlags -> Description -> get -> set -> OptDescr a

これは MkOptDescr get set a 型が5つの引数を持ち、値が OptDescr a 型を戻す関数であることを示している。MkOptDescr get set a の get set a は引数ではなく型変数だ。これは、get, set, a 型が多相性であることを示している。

上の type 宣言で SFlags はショートオプションの文字のリスト、LFlags はロングオプションの文字列のリスト、Description はオプションの説明の文字列、get はフラグセットのフラグの値を取り出す関数、set はフラグセットのフラグの値を設定する関数だ。

これらの引数は OptDescr 型の全てのコンストラクタのパラメータを作るために共通の要素だ。OptionField 型を作る option 関数は、MkOptDescr 型の関数を引数として受け取ることで、OptDescr 型の4つのコンストラクタ全てに対し統一的な操作をおこなうことができる。

この後に調べるこのセクションの関数は全て MkOptDescr get set a 型の関数を作成するための関数だ。そうして、MkOptDescr set get a 型の関数は、option 関数の引数に利用されて、option 関数の値である OptionField 型のパラメータに含まれる OptDescr 型のデータの作成に利用される。

option 関数の型を次に示すが、option 関数の 6 番目の引数が MkOptDescr get set a 型の関数だ。

ghci> :t option
option
  :: SFlags
     -> LFlags
     -> Description
     -> get
     -> set
     -> MkOptDescr get set a
     -> OptionField a

これを見ると MkOptDescr get set a という不可思議なデータ型の別名が、急に複雑なコードを簡潔に表現するための巧妙な工夫に見えてくる。ソースコードを読む面白さと難しさはここにある。ソースコードの著者の意図をつかむと非常にスッキリした設計思想があるのに気づくが、著者の意図を読み誤ると理解不能な迷路に迷いこんでしまう。しかも、ソースコードには著者の意図はあまり詳しく書かれていないことが多いので、それは読むほうが推理していかなくてはならない。

reqArg

reqArg は ReqArg コンストラクタの用の MkOptDescr 型関数を作成するための関数だ。reqArg の型は次のようになる。option 関数で OptionField 型を作成する時、その引数として MkOptDescr set get a 型の関数を要求する。その関数を作成するための関数だ。

ghci> :t reqArg
reqArg
  :: Data.Monoid.Monoid b =>
    ArgPlaceHolder
    -> Distribution.ReadE.ReadE b
    -> (b -> [String])
    -> MkOptDescr (a -> b) (b -> a -> a) a

これはつぎのような ReqArg の型とずいぶん違う。

ghci> :t ReqArg
ReqArg
  :: Description
    -> OptFlags
    -> ArgPlaceHolder
    -> Distribution.ReadE.ReadE (a -> a)
    -> (a -> [String])
    -> OptDescr a

reqArg 関数の第1引数の ArgPlaceHolder 型の(String 型の別名) 値はそのまま ReqArg コンストラクタの ArgPlaceHolder 型のパラメータに引き継がれる。reqArg の第1引数には次に述べる reqArg 関数の定義では ad という名前が割り当てられている。

reqArg 関数の第2引数は Distribution.ReadE.ReadE b 型だがこれは ReqArg コンストラクタの Distribution.ReadE.ReadE (a -> a) 型とは異なっている。この第2引数は mkflag という名前が割り当てられている。これはフラグを作成する関数という意味だ。

reqArg 関数の第3引数は (b -> [String]) 型で、ReqArg コンストラクタの (a -> [String]) パラメータと型が一致している。この第3引数には showflag という名前が割り当てられていて、フラグ b の値を文字列のリストにするという意味がある。

mkflag と showflag の引数はそのまま ReqArg コンストラクタのパラメータになるのではなく reqArg 関数内で処理された後、ReqArg コンストラクタのパラメータに割り当てられる。

reqArg のソースは次のようになる。

-- | Create a string-valued command line interface.
reqArg :: Monoid b => ArgPlaceHolder -> ReadE b -> (b -> [String])
                    -> MkOptDescr (a -> b) (b -> a -> a) a
reqArg ad mkflag showflag sf lf d get set =
  ReqArg d (sf,lf) ad (fmap (\a b -> set (get b `mappend` a) b) mkflag) (showflag . get)

reqArg は 8 引数関数として定義されているが、実際に引数として与えられるのは ad, mkflag, showflag の3つだ。したがって reqArg 関数の戻値は sf, lf, d, get, set の 5 引数関数になる。つまり MkOptDescr get set a 型の関数だ。引数の一部分に値を割り当てることによって新しい関数を作るやり方は、Haskell の関数の部分適用を利用している。

reqArg の引数のうち ad は ArgPlaceHolder、sf はショートオプションのリスト、lf はロングオプションのリストで sf と lf のペア (sf, lf) は OptFlags、d は Description に相当する。Distribution.ReadE.ReadE (a -> a) に相当するのが (fmap (\a b -> set (get b `mappend` a) b) mkflag) で、(a -> [String]) に相当するのが (showflag . get) だ。

また、set, get, mkflag, showflag などの関数は引数として与えられるので、特別な関数を使っているわけではない。

上のコードに使われている関数のうち、fmap は Functor クラスの多相関数で、第2引数のコンストラクタのパラメータをコンストラクタに収めたまま第1引数の関数で操作するためのものだ。

mappend 多相関数は Monoid クラスのインスタンスのデータの2項演算をするためのものだ。

reqArg は、その値の MkOptDescr get set a 型の関数が option 関数の引数として利用される。6番目の MkOptDescr get set a 型の引数がそれだ。したがって reqArg 関数の用例を Distribution.Simple.Setup に探したが reqArg 関数を使って OptDescr 型の値を直接的に生成するものはなかった。

ghci> :t option
option
  :: SFlags
     -> LFlags
     -> Description
     -> get
     -> set
     -> MkOptDescr get set a
     -> OptionField a

reqArg の使い方は実例を見た方がいいが、Distribution.Simple.Setup のソースを見ても reqArg をダイレクトに使って OptDescr 型のデータを作成している例はない。reqArg の値である MkOptDescr 関数は option 関数の引数に使われて、OptionField 型のデータが作成される。OptDescr 型のデータは OptionField 型に含まれるが、明示的に OptDescr 型のデータが作成されるわけではないからだ。

さて、前回の記事で installCommnad には builddir オプションに対応する ReqArg コンストラクタの OptDescr 型データが含まれているのが分かっている。

ghci> map optionName $ commandOptions installCommand ShowArgs
["verbose","builddir","inplace","shell-wrappers","package-db"]

Distribution.Simple.Setup の installCommnad のソースは次のようになっている。

installCommand :: CommandUI InstallFlags
installCommand = makeCommand name shortDesc longDesc defaultInstallFlags options
  where
    name       = "install"
    shortDesc  = "Copy the files into the install locations. Run register."
    longDesc   = Just $ \_ ->
         "Unlike the copy command, install calls the register command.\n"
      ++ "If you want to install into a location that is not what was\n"
      ++ "specified in the configure step, use the copy command.\n"
    options showOrParseArgs =
      [optionVerbosity installVerbosity (\v flags -> flags { installVerbosity = v })
      ,optionDistPref
         installDistPref (\d flags -> flags { installDistPref = d })
         showOrParseArgs

      ,option "" ["inplace"]
         "install the package in the install subdirectory of the dist prefix, so it can be used without being installed"
         installInPlace (\v flags -> flags { installInPlace = v })
         trueArg

(以下省略)

このソースの options showOrParseArgs の定義に使われている optionVerbosity, optionDistPref, option などの関数が OptionField 型のデータを作成する関数だ。したがって、その関数の機能の中に OptDescr 型のデータの作成が含まれている。

reqArg 関数はこれらの関数のどれかに引数として使われているはずだが、このソースには現れていない。そこでこれらの関数が出現する順序と builddir オプションの出現順位を比較してみた。

出現順位の比較から builddr の OptionField を作成する関数は opiton 関数ではなく optionDistPref が使われているのが分かる。optionDistPref 関数は Distribution.Simple.Setup に定義されている。optionDistPref 関数のソースは次のようになる。

optionDistPref :: (flags -> Flag FilePath)
               -> (Flag FilePath -> flags -> flags)
               -> ShowOrParseArgs
               -> OptionField flags
optionDistPref get set = \showOrParseArgs ->
  option "" (distPrefFlagName showOrParseArgs)
    (   "The directory where Cabal puts generated build files "
     ++ "(default " ++ defaultDistPref ++ ")")
    get set
    (reqArgFlag "DIR")
  where
    distPrefFlagName ShowArgs  = ["builddir"]
    distPrefFlagName ParseArgs = ["builddir", "distdir", "distpref"]

ここでもまだ reqArg 関数は現れない。しかし、optionDistPref 関数が内部的に option 関数を呼び出して OptionField 関数を作り出しているのがわかる。option 関数の型を再掲すると次のようになる。

option
  :: SFlags
     -> LFlags
     -> Description
     -> get
     -> set
     -> MkOptDescr get set a
     -> OptionField a

option 関数の MkOptDescr get set a 型の引数が reqArg になるはずだ。しかし、optionDistPref のソースではその部分が (reqArgFlag "DIR") になっている。reqArgFlag のソースは Distribution.Simple.Setup にあり、次のようになる。

reqArgFlag :: ArgPlaceHolder -> SFlags -> LFlags -> Description ->
              (b -> Flag String) -> (Flag String -> b -> b) -> OptDescr b
reqArgFlag ad = reqArg ad (succeedReadE Flag) flagToList

やっと reqArg が出てきた。reqArg の型を再掲する。

ghci> :t reqArg
reqArg
  :: Data.Monoid.Monoid b =>
     ArgPlaceHolder
     -> ReadE b
     -> (b -> [String])
     -> MkOptDescr (a -> b) (b -> a -> a) a

:type コマンドで表示される reqArg の引数は3つだ。第1は ArgPlaceHolder をあらわす文字列。第2はフラグセット b の特定のフラグの値を設定する mkflag 関数。第3はフラグセットの特定のフラグの値を取り出す showflag 関数だ。そうして、戻り値は MkOptDescr 型の関数だ。

reqArg 関数のソースを再掲すると次のように8個の引数をとるが、ad, mkflag, showflag に部分適用することによって5引数の関数を値として戻すことになる。

reqArg ad mkflag showflag sf lf d get set =
  ReqArg d (sf,lf) ad (fmap (\a b -> set (get b `mappend` a) b) mkflag) (showflag . get)

reqArg の引数のうち第1引数の ad はArgPlaceHolder になる。

第2引数の mkflag 関数の (succeedReadE Flags) のうち succeedReadE は String -> a 型の関数を引数に取り、ReadE 型のパーサを返す関数。ソースは次のようになる。

succeedReadE :: (String -> a) -> ReadE a
succeedReadE f = ReadE (Right . f)

Distribution.ReadE をインポートして ghci で試してみた。

ghci> import Distribution.ReadE
ghci> runReadE (succeedReadE (\s -> "foo " ++ s)) "bar"
Right "foo bar"

(succeedReadE f) 関数は上の例の場合 String -> String 型の関数を引数に取る。これは ReadE 型のパーサになるので、runReadE アクセサの引数にする事で文字列 "bar" をパースすることができる。パースの結果は、Right "foo bar" として返されている。

したがって、reqArgFlag の定義に現れた reqArg に第2引数として与えられた (suceedReadE Flag) という関数は、文字列 str をパースして Right (Flag str) を返す ReadE 型のパーサであることが分かる。

ghci> runReadE (succeedReadE Flag) "foo"
Right (Flag "foo")

つまり、reqArg 関数の第2引数である mkflag 関数は文字列を Flag 型にラッピングしてさらにそれを ReadE 型のパーサとして返す関数である。

reqArg の第3引数の showflag 関数の flagToList 関数は Distribution.Simple.Setup にあり、ソースは次のようになる。Flag 型のパラメータを取り出して、その値を要素とするリストを作るだけの関数だ。

flagToList :: Flag a -> [a]
flagToList (Flag x) = [x]
flagToList NoFlag = []

flagToList は ghci で試してみることができる。

ghci> flagToList (Flag "foo")
["foo"]

したがって showflag 関数はフラグにラッピングされたパラメータをリストとして返す関数であることが分かる。

mkflag, showflag 引数の意味が分かってきたので reqArg の get, set 引数がどのように ReqArg コンストラクタの get, set パラメータになるのかを見てみる。そのため reqArg の定義を再掲する。

reqArg ad mkflag showflag sf lf d get set =
  ReqArg d (sf,lf) ad (fmap (\a b -> set (get b `mappend` a) b) mkflag) (showflag . get)

これを見ると RecArg コンストラクタの set パラメータ

(fmap (\a b -> set (get b `mappend` a) b) mkflag)

で、get パラメータ

(showflag . get)

であることが分かる。set や get の実例を見るために installCommand のソースの bilddir のオプションの部分を再掲する。

      ,optionDistPref
         installDistPref (\d flags -> flags { installDistPref = d })
         showOrParseArgs

これから、reqArg の引数のget が

installDistPref

で、reqArg の引数の set が

(\d flags -> flags { installDistPref = d })

であることが分かる。これらが reqArg 関数の引数の get, set になるわけだが、それがどう ReqArg の get, set パラメータになるのだろうか。

まず簡単な get パラメータの方から見てみよう。reqArg の get 引数は showflag 関数と組み合わされて showflag . get が ReqArg の get パラメータになる。installDistPref は InstallFlags の installDistPref フィールドのアクセサだから defaultInstallFlags から installDistPref フィールドの値を取り出すことができる。

ghci> installDistPref defaultInstallFlags
Flag "dist"

これは Flag 型にラッピングされているので showflag である flagToList 関数と合成することでパラメータを文字列のリストとして取り出すことができる。

ghci> (flagToList . installDistPref) defaultInstallFlags
["dist"]

set パラメータの方はやや複雑だ。まず reqArg 関数の引数の set である (\d flags -> flags { installDistPref = d }) について見てみる。これは2つの引数 d, flags をとり flags の installDistPref を d に設定する働きをしている。

ghci> (\d flags -> flags {installDistPref = d}) (Flag "foo") defaultInstallFlags
InstallFlags {installPackageDB = NoFlag, installDistPref = Flag "foo", installUseWrapper = Flag False, installInPlace = Flag False, installVerbosity = Flag Normal}

この関数を reqArg の set 引数に与え、ReqArg コンストラクタの set パラメータにするためには次の処理が必要だ。

(fmap (\a b -> set (get b `mappend` a) b) mkflag)

上のソースの検証ができるように myget, myset, mymkflag を次のように定義する。

ghci> let myget = installDistPref
ghci> let myset = (\d flags -> flags {installDistPref = d})
ghci> let mymkflag = (succeedReadE Flag)

ghci> myget defaultInstallFlags
Flag "dist"
ghci> myget $ myset (Flag "foo") defaultInstallFlags
Flag "foo"
ghci> runReadE mymkflag "bar"
Right (Flag "bar")

コードの括弧の入れ子の中から1つずつ検証してみる。mappend は Data.Monoid の関数だからそれをインポートする。さらに、 Flag 型と Flag 型の mappend による二項演算を調べる。

ghci> import Data.Monoid
ghci> Flag "foo" `mappend` Flag "bar"
Flag "bar"

Flag String 型の mappend の場合は新しいデータへの置き換えになる。

set (get b `mappend` a) b)

は、フラグセット b の値を取り出して Flag a に置き換えてフラグ b にセットする。

ghci> myget $ myset (myget defaultInstallFlags `mappend` (Flag "foo")) defaultInstallFlags
Flag "foo"

fmap はこれらの操作をフラグセットの要素をコンストラクタにラッピングしたままで操作する事ができる。コードの全文を再現して動作を検証したら、installDistPref フィールドの値をパースした文字列で置き換えることができた。

ghci> let Right f = runReadE (fmap (\a b -> myset (myget b `mappend` a) b) mymkflag) "foo"
ghci> myget $ f defaultInstallFlags
Flag "foo"
[PR]
by tnomura9 | 2014-02-01 16:29 | Haskell | Comments(0)

1 = 0.999... か?

以前から不思議に思っていた事がある。 1 = 0.9999... は正しいのだろうかということだ。小数点下のどの桁の数字を比べてみても 1 と 0.99999...は違う。こういうものを同じであると言っていいのだろうか。しかしその差をとってみると 1 - 0.99999... = 0.00000... となってはっきりと差を認めることができない。差が認められないのなら同じ物といいのではないだろうか。

だが、その場合でも 0 と 0.00000... が同じ物なのかという疑問が発生する。

自分の過去記事で飛ぶ矢のパラドックスについて書いた物を読んでいたら、その解決法を思いついた。ゼノンの論理では、ある物体が空間の一点を占めているときにはその物体は静止している。したがって、矢はどの瞬間も静止しているので、飛んでいる矢も静止しているというものだ。しかし、実際は飛んでいる矢は動いているので静止してはいない。それがパラドックスになってしまう。

これに対する反論は瞬間という物はないというものだ。シャッタースピードの遅いカメラで飛んでいる鳥を撮影すると像が重なってぼんやりした像になる。シャッタースピードが速ければ、いかにも飛んでいる鳥が静止したような写真がとれるが、これは単に像のぶれが少ないために、一見静止しているように見えるだけで、拡大すればシャッターが上がったときからシャッターが下りるまでの像が重なっているはずだ。

本当に瞬間の像を撮ろうと思ったら、カメラのシャッターは開いていると同時に閉じていなくてはならない。それ以外の場合は瞬間の像をとらえることはできない。しかし、シャッターが開いていると同時に閉じている事は矛盾している。したがって、瞬間の像をとるということは不可能なのだ。

しかし、シャッタースピードをどこまでも短くしていけば、撮影した写真はどこまでもぼけのない瞬間の像に近づける事ができる。飛ぶ鳥の瞬間の静止画像とは、このような極限の画像なのだ。

つまり、連続量に関する限り、一点という物はない。どの数もその周囲に無限小をまとった極限と考えるべきだ。こう考えると冒頭の疑問に答えることができる。1 と 0.99999... は10進数表記では明らかに異なる数だ。しかし、その差を求める事はできない。それらの極限値が一致しているからだ。両者はどちらもその周りに無限小をまとった極限を表しているからだ。同じとか違うという概念について離散数と同じ考え方を連続量に適用する事はできない。離散量では一点というものが存在するが、連続量の一点は極限としてしか存在しないからだ。

上の例の 1 は整数の 1 と同一視する事ができるが、0.999... は普通の数ではなく、0.9 + 0.09 + 0.009 + ... という無限級数だ。そうしてその極限が 1 なのだ。1 = 0.999... は両者の極限が一致している事を示している。連続量の等しさは、このような極限同士が等しいということを意味している。

連続量では一点というものを無現小の幅を持った極限と考えなければならない。実数内の整数の 1 や 2 は一点のように見えるかもしれないがそれは離散量のイメージを連続量に無理矢理当てはめただけだ。そのためにゼノンの論理のようなパラドックスが発生してしまうのだろう。

実数ではすぐ隣の数というものは存在しないので、整数の等しさを実数に持ち込む事はできない。実数における等しさとは、極限が等しいという意味の等しさだからだ。したがって、整数と違い十進数表記が一致しなくとも同じ数を表すことがある。
[PR]
by tnomura9 | 2014-02-01 08:08 | 考えるということ | Comments(3)