リファクタリング 第1章

問題

  1. 構造的に機能を付け加えにくいプログラムに新規機能を追加しなければならない場合、どうすればよいか。
  2. リファクタリングをする時に、最初にすることは何か。
  3. メソッドを抽出した際に、抽出したコードの責務が元のクラスにないことがわかった。メソッドの移動をさせたいが、はじめに抽出したメソッドを消す前に、どのような過程を挟むのが安全か。
  4. メソッドの中だけで有効なローカル変数、一時変数は少ない方が良い。それはなぜか。
  5. 4の解決策は何か。
  6. リファクタリングによってメソッドの持つ責務を別々のメソッドに切り分けたことで、外部にどのような利点が生じうるか。
  7. 他のオブジェクトの属性を調べるswitchやif文を書くことは常に間違いであるが、それはなぜか。
  8. switch, ifによるオブジェクトの属性の分類は、同じ質問に対してオブジェクトが異なる処理をすることである。これはサブクラスの処理と考えられ、継承が利用可能である。しかし、オブジェクトの属性が度々変わる場合、それに合わせて派生先のサブクラスを変更することは出来ない。どうすればよいか。
続きを読む

LYH 9.1 ファイルとストリーム

問題

(1) foreverについて、型シグネチャと用途、importすべきものを言え。

(2) toUpperについて、importすべきものを言え。

(3) capslocker.hsを実装せよ。

(4) getContentsは遅延I/Oであり、入力が大きい場合は(  )消費量の面で有利である。

(5) contents <- getContentsによって、contentsにgetContentsの結果が束縛されるとき、最終的に文字列として評価される(  )としてメモリ上に置かれる。

(6) 10文字より短い行だけ出力するプログラムを作れ。

(7) interactとはどのような関数か。

(8) inretactを用いて、入力した文字列の回文判定をせよ。

続きを読む

入門ソーシャルデータ 1.1, 1.2

※本記事では、書籍を読んだときの自己解釈によるまとめや感想を記述しています。実際の内容とは異なりますのでご注意下さい。

1.1 概要

TwitterAPIを概観し、頻度分析を使ってツイートから何らかの分析結果を導き出し、データマイニングの基礎を学ぶ。

学ぶこと

1.2 Twitterはなぜ人気があるのか

Twitterは相互フォローが前提ではなく、一方的なフォローモデルで構築されている。一方的なフォローは、好奇心・関心の矢印に言い換えられる。

インタレストグラフ(関心の結びつき)としてTwitterを考える。このとき各ノードが本物の人間でなくてもよい。このグラフによって関心を持つ人の集合を特定できる。どのようなコミュニティが存在しうるかも分かる。

Twitterは、規則として組織化を強制しようとしない。特定できる人物であったり、特定の団体に所属していたりする必要がない。代わりに、少しのツールによって、ツイートの自己組織化を支援する仕組みが備わっている。

例えばハッシュタグというツールが存在する。このタグで検索をかければ、数多のツイートからタグが付随するツイートのみを抽出することが出来る。この機能は、ユーザにとってすぐに理解でき、ユーザに様々な利用方法の想像を掻き立たせ、ユーザがすぐに使うことが出来るものである。ハッシュタグに具体的な利用方法の制限が付随して、正しくツイートを組織化する方法をユーザに強制することはない。ユーザ間や利用するコミュニティによって、組織化の方法が自ずと定まってくるシステムになっている。個々の要素を分類学的な方法(タクソノミー)で区別するのではなく、個々人の勝手なラベル付の判断によって区別されていく方法(フォークソノミー)が用いられている。

LYH 8.3 I/O アクションどうしをまとめる

8.3 I/O アクションどうしをまとめる

main = do
    putStrLn "Hello, what's your name?"
    name <- getLine
    putStrLn $ "Zis is your future: " ++ tellFortune name

8.1と説明が重複するところがあるが、もう一度書く。

上のコードでは、getLineが副作用を扱っているものの、Stringを束縛したnameが関わるいかなる関数においてもI/Oについての情報を知る必要はない。上のtellFortuneという関数はString -> Stringという型シグネチャを持っているものとする。確かにI/Oの影響が及ぶ箇所はない。副作用が発生する部分は、getLineの中だけに留まっている。このIOアクションによって生成された結果が<-によって取り出されてnameに束縛されてからは、副作用のない世界でnameが処理されるようになる。

IOアクションは、実行されるまで不活性なままである。Haskellではmainという、ただ一つのIOアクションが実行される。mainが1つのIOアクションのみで定義されるなら、

main = some IO action

となるし、doブロックでまとめれば

main = do
    (IO action 1)
    (IO action 2)
    ...
    do
        (IO action x)
        (IO action y)
        ...

と記述することも出来る。

また、main以外でも、I/Oアクションが実行される場合がある。GHCiにI/Oアクションを入力してENTERキーを押した時である。

ghci> putStrLn "HEEY"
HEEY

以下のようなコードは書けるだろうか。

nameTag = "Hello, my name is " ++ getLine

型を考えてみよう。"Hello, my name is "の型は、String すなわち [Char] であるが、getLineの型はIO Stringである。 CharのリストとIO Stringとでは型が違うので、当然、リストの連結を行うことは出来ない。よって、このコードは間違いである。

I/Oアクションはどれも結果を生成する。

main = do
    foo <- putStrLn "Hello"
    name <- getLine
    putStrLn ("Hey " ++ name ++ "!")

上のコードは間違いではないが、putStrLn "Hello"の型はIO ()であるため、fooに束縛した値は()であり、意味がない。 また、doブロックの最後の行は束縛を書くことは出来ない。この理由は「13章モナドがいっぱい」で分かるらしい。

以下の文は、getLineをmyGetLineに変えただけで、決してgetLineからStringを取り出してmyGetLineに束縛したものではない。

let myGetLine = getLine

無意味なコードだが、上の文を書いたあとには以下のような記述が可能となる。

name <- myGetLine

8.3 確認問題

  1. 複数のI/Oアクションを糊付けして1つにするには何を用いればよいか。
  2. "Hello, what's your name?"と一行に出力し、続いて文字列として名前を読み取らせ、最後に、"Hey (名前), you rock!"と一行に出力せよ。
  3. 2においてdo構文でまとめられたI/Oアクションの型は何か。
  4. 3の型になる理由を答えよ。
  5. mainの型シグネチャはどんな形をしているか答えよ。
  6. getLineの型を答えよ。
  7. putStrLnの型を答えよ。
  8. doブロック内で、getLineをmyGetLineという名前で使えるようにするにはどうすればよいか。
続きを読む

LYH 8.2 Hello, World!

8.2 Hello, World!

main = putStrLn "hello, world"

これをhelloworld.hsとして保存し、

$ ghc --make helloworld

と入力する。コンパイルされて、helloworldという実行ファイルが作成される。

8.2 確認問題

  1. putStrLnの型を調べよ。(:t putStrLnの結果を示せ)
  2. putStrLn "hello, world"の型を示せ。
  3. putStrLnの型を言葉で説明せよ。
  4. helloworld.hsをコンパイルし、helloworldという実行ファイルを作成するコマンドを示せ。
  5. 実際にI/Oアクションが行われるのはいつか。
続きを読む

LYH 8.1 入出力

8.1 入出力

8.1 不純なものと純粋なものを分離する

Haskellでは関数が副作用を持つことが許されない。木のようなデータ構造でさえ、要素を追加しようと思ったら、新しい要素をが追加された木を、新たに変数に束縛することで対応できた。しかし、入出力に関しては、どうやっても状態の書き換えが必須となる。ディスプレイの状態が書き換わらずにどうやってユーザが扱うことが出来るだろうか。また、一行の入力を求める関数があったとき、ユーザの入力は毎回異なるから、関数が生成する文字列は毎回異なる。どう対処すればよいか。

副作用が含まれる処理が、副作用を含まない処理に影響を与えなければ良い。副作用が含まれる処理の中で完結していれば、副作用を含む処理と含まない処理を分離して扱うことができる。

以下の一文を考えよう。

name <- getLine

前提知識がない状態だと、「一行を読み取って、変数に読み取った文字列を代入している」と読めそうである。

もしそうであれば、getLineの型は、getLine :: String だろうか。実際に:tで確かめてみると以下のように表示される。

getLine :: IO String

ここで、IO String は、Stringを生成するI/Oアクションと読むことが出来る。

生成されるStringは毎回同じではないため、getLineに副作用が存在している。しかし、I/Oアクションによって生成されたStringをnameに束縛していると考えると、Stringをnameに束縛する作業は副作用がない処理と変わりはない。I/Oアクションによって生成されたStringを置いておく箱から、Stringを取り出してくる処理として<-を用いることで、副作用のある処理と副作用のない処理を分離して考えることが出来る。

8.1 確認問題

  1. I/Oアクションとは何か。
  2. I/Oアクションがどうやって入出力を可能にするのか。
  3. 純粋関数型言語によって得られる利点を3つあげよ。
続きを読む