真面目なブログはこっち 👉 blog.s64.jp

蟻本第2版34ページの問題をHaskellで書く

蟻本第2版23ページの問題 (POJ 1852) をHaskellで書く - 🍥shuma_yoshiokaに引き続き、プログラミング学び直し日記。「プログラミングコンテストチャレンジブック 第2版」の34ページより、「部分和問題」。類似問題はこちら。

プログラミングコンテストチャレンジブック [第2版] ?問題解決のアルゴリズム活用力とコーディングテクニックを鍛える?

プログラミングコンテストチャレンジブック [第2版] ?問題解決のアルゴリズム活用力とコーディングテクニックを鍛える?

f0 :: [Int] -> Int -> Bool
f0 (a:as) k = a == k || f0 as k || f1 a as k
f0 [] _ = False

f1 :: Int -> [Int] -> Int -> Bool
f1 x (y:ys) k = x + y == k || f1 (x+y) ys k
f1 _ [] _ = False

main :: IO ()
main = do
    _ <- readLn :: IO Int -- n
    as <- (map read . words) <$> getLine :: IO [Int]
    k <- readLn :: IO Int

    putStrLn $ if (f0 as k) then "Yes" else "No"

コレで合ってるか大変不安である。しかし参考ナシで例を一発正当できたので嬉しい。

蟻本第2版23ページの問題 (POJ 1852) をHaskellで書く

蟻本第2版21ページの問題をHaskellで書く - 🍥shuma_yoshiokaに引き続き、プログラミング学び直し日記。「プログラミングコンテストチャレンジブック 第2版」の23ページより、「Ants」の問題。類似問題はこちら。

プログラミングコンテストチャレンジブック [第2版] ?問題解決のアルゴリズム活用力とコーディングテクニックを鍛える?

プログラミングコンテストチャレンジブック [第2版] ?問題解決のアルゴリズム活用力とコーディングテクニックを鍛える?

fMin :: [Int] -> Int -> Int
fMin (x:xs) l = maximum [ minimum [x, l-x], (fMin xs l) ]
fMin [] _ = minBound

fMax :: [Int] -> Int -> Int
fMax (x:xs) l = maximum [(l-x), (fMax xs l)]
fMax [] _ = minBound

main :: IO ()
main = do
    l <- (readLn :: IO Int)
    _ <- (readLn :: IO Int) -- n
    xs <- (map read . words) <$> getLine :: IO [Int]

    let r = ( fMin xs l , fMax xs l )

    putStrLn $ "min = " ++ show (fst r) ++ "\nmax = " ++ show (snd r)

この回答自体はとてもアッサリで、問題文から導き出す過程が大変。解説文ナシで解く技量はまだ無い。

蟻本第2版21ページの問題をHaskellで書く

FPについて学び直しメモ - 🍥shuma_yoshiokaに引き続き、プログラミング学び直し日記。「蟻本」こと「プログラミングコンテストチャレンジブック 第2版」を読みながら、問題をHaskellで解いてゆく試み。

プログラミングコンテストチャレンジブック [第2版] ?問題解決のアルゴリズム活用力とコーディングテクニックを鍛える?

プログラミングコンテストチャレンジブック [第2版] ?問題解決のアルゴリズム活用力とコーディングテクニックを鍛える?

21ページより「三角形」の問題。類似問題はこちら。

f1 (x:xs) = maximum [(f2 x xs), (f1 xs)]
f1 [] = 0

f2 x (y:ys) = maximum [(f3 x y ys), (f2 x ys)]
f2 _ [] = 0

f3 x y (z:zs) = maximum [(f4 (maximum [x, y, z]) (x + y + z)), (f3 x y zs)]
f3 _ _ [] = 0

f4 max len = if (len - max) > max then len else 0

main :: IO ()
main = do
    _ <- (readLn :: IO Int) -- n
    as <- (map read . words) <$> getLine :: IO [Int]
    print $ f1 as

ほぼ初めてということもあり、ループを使わず再帰脳づくりするのにかなり時間掛かった

FPについて学び直しメモ

OOPについて学び直しメモ - 🍥shuma_yoshioka に引き続き、雰囲気でプログラミングしているのでFP (Functional Programming, 関数型プログラミング) について雑に学び直した。
学び直しとは言うけど、そもそも関数型プログラミングは勉強したことないし、もっと言うとちゃんとHaskell書いたのも初めてになる。

手続型言語と関数型言語の違い

明確な定義は無いらしいが、言語自体に 関数の再帰呼出しによるプログラムの数式化 を助ける機構があるか否か、がポイントとなるもよう。

Javaはオブジェクトを手続型のように扱えるのがコンセプトとなる言語なので、このような言語は オブジェクト指向型言語 などのように分類されやすい (手続き型言語として分類されにくい) 。よって、手続き型言語には例えば PHP, Python などが挙げられやすい。
対し関数型として挙げられやすい言語は、Lisp, Haskell, OCaml, Erlang, Scala など。(Scalaはハイブリッドと言われがちだが)

Javaが「全てがオブジェクトである」のと同じように、関数型言語は「全てが数式である」と言える。
オブジェクト指向言語は、たとえ同じメソッドであってもそのオブジェクトが持つ状態により暗黙的に値が変わる場合が多々ある。
あまり良い例とは言い難いが、パッと出せるとこで言うと例えば、

class MyCls {
  
  private final int state1;
  private final int state2;

  public MyCls(int state1, int state2) {
    this.state1 = state1;
    this.state2 = state2;
  }

  public int calcState(int x) {
    return x * (state1 + state2);
  }
}

// App.java
public class App {

  public static void main(String[] args) {
    MyCls obj = new MyCls(2, 3);
    System.out.println(obj.calcState(5)) // 上の行を知らないと、ここの式で何が出力されるかわからない
  }
}

というのが発生する。これは決して常に悪く作用するわけではないが、参照透過性が無いと言える。
参照透過性は、入力が同じなら同じ作用と同じ値を返す状態で成立する。つまり手計算した値を直接埋め込んでも大丈夫な状態でなければいけない。

対し関数型言語とし例えばHaskellの場合、以下のようにするだろう。

data MyState = MyState { state1 :: Int, state2 :: Int }

calcState x y = x * (state1 y + state2 y)

main = do
  let my = MyState { state1 = 2, state2 = 3 }
  print $ calcState 5 my

このような参照透過性を実現する強い制約が関数型言語をそれたらしめていると言える。
そしてこのような制約のおかげで、再帰呼出しによる数式化が助けられている。

fibSeq x y = [x] ++ fibSeq y (x + y)
-- または `fibSeq x y = x : fibSeq y (x + y)`

main = do
  print $ take 10 $ fibSeq 0 1

{-
  フィボナッチ数列を求める簡単な例。
  読み方:
    1. 第一引数のみを持った配列をつくる
    2. xとyを足し合わせたものを求める
    3. 再帰呼び出しで配列を取る。第一引数はy、第二引数はさきほど足し合わせたもの
    4. それらの配列を連結する。第一引数の次に第二引数という風に結合され、さらにそれを足し合わせたもの、という形に無限に求め続ける
    5. `take 10`を使い、先頭から10番目の要素までを取り出す (現れた時点で辞める)
    6. それを標準出力へ
-}

なおパターンマッチなども特徴的だが、これらはあくまでも言語機能、糖衣構文のようなものでしかないので深くは言及しない。

関数型言語のエッセンス

関数型言語の特徴として、前述のとおり数式化可能なこと、参照透明性が高いことが挙げられる。
そうした特徴は様々な非関数型言語へも取り入れられている。たとえば、JavaのStream, Reactive Extensionsなども関数型言語の影響を受けている。

関数を引数とした関数を高階関数 (ハイオーダー ファンクション) という。Reactive ExtensionsなどのRxと呼ばれるライブラリは、FRP (Functional Reactive Programming) を実現するもの。Reactive、Observableは非同期プログラミングにおける概念なので、ここでは深く言及しない。
またKotlinなどはCollectionsとして同様の高階関数を提供している。

おしまい

他にも触っておくべきことが多そうだが、恥ずかしながら関数型言語の利用経験が少ないためすぐに思いつかなかった。そのため一旦ここで区切ることにする。
参考にした記事は、

など。

OOPについて学び直しメモ

雰囲気でプログラミングをしているので、OOP (Object-Oriented-Programming, オブジェクト指向プログラミング) について雑に学び直した。

オブジェクトとは, OOPの基本

オブジェクトとはモノのこと。オブジェクト指向とは、モノを操作する (操作できるモノを作る) プログラミングの方向性のこと。

モノには様々あるが、例えばリモコンやゲームコントローラなどのアクションを起こせる物体を想像すると良い。
可能な操作が一覧されており、命令を行うと内部の仕組みを知らなくても (隠蔽されていても) 意図した動作になる。
そうした「リモコン」と「テレビ」、「ゲームコントローラ」と「ゲーム機」のようなモノとモノ同士、つまりオブジェクトの操作を組み合わせてプログラミングしていこうという、現実世界での任務遂行方法を模倣したプログラミング手法と言える。

カプセル化 (エンカプセレーション)

OOPにおける特徴的な概念として、カプセル化が挙げられる。
前述のとおり、リモコンやゲームコントローラの使い方は知っていても、その内部の仕組みは知らないし、知らなくても問題なく利用できる。
逆に直接基盤などへ触れられるようになっていると、意図されていない操作で破壊してしまう可能性がある。
そのような操作を防ぐため、利用者向けにはボタンで操作を、ランプで状態の確認をできるようにしている。

カプセル化とはつまり、オブジェクトの持つフィールドを直接操作する必要がないよう、メソッドで覆うことを指す。これにより、外側の操作を変えずとも、メソッドの実装で実際のフィールドに対する操作を安全に、あらゆる箇所で行える。

継承 (インヘリタンス)

継承関係とは、要はIs-a関係の話である。つまり、B extends Aの場合、BはAの一種 (亜種)であると判断し、Aとして扱うこともできることの話である。
具体例で言うと、Viewの一種としてButtonやProgressBarが存在し、これらには例えば共通の「描画位置情報を返しなさい」というメソッドがあるかもしれない。それらは恐らく各々の描画情報さえあればそれを元に計算が可能であり、別の実装を必要としない。そういった共通の処理を "継承" することを指す。

紛らわしいが、共通の "メソッド名" で各々の異なる実装を呼べることは多態性になるため継承ではない。継承で話しているのはinterfaceの話である。overrideではない。

もう少し現実寄りの例にする。例えば "果物" の種類として "リンゴ", "バナナ" があるとする。果物の定義に確固たるものは無いが、たとえば ”草木の実である" とする。
つまり裏を返せばリンゴとバナナには共通処理として「木から実る機能」が存在することになる。これを各果物ごとに実装すると、人的ミスにより定義が揺らぎかねない。もしかしたら間違えて土から生やしてしまうかもしれない。

なので基底となる「果物」自体に木から実る機能を実装してしまえば、手間も掛からずミスも防げる。

多態性 (ポリモーフィズム)

多くのリモコンやゲームコントローラは、たとえ機種が異なっても説明書を読まずに利用できる。その理由には、同様のラベル、同様の形状など、説明を不要とする工夫が挙げられる。そしてそのお蔭で、知らずとも期待した動作をする。
しかしながら実際には、ボタンを押す操作により送出される信号は機種により異なる。もちろん画面上の表示も異なるだろう。

このように具体的な中身の処理を知らずとも様々な処理が起こりうるのが多態性である。同様のラベルを持つものに意味付けをし、具体的な処理は実装に任せることができる。

カプセル化で気をつけること (ワイの経験則)

カプセル化すればなんでも隠蔽できると思っちゃダメ。あくまでも1つのオブジェクト (クラス) は1つの役割だけを持つようにする。例えば、データ構造を持つ、他のデータ構造にマッピングする、該当するデータ構造を複数の永続化機構のいずれかから取り出す、など。
リンゴは自身の特徴や状態だけを持つと期待されるため、もしキャッシュ機能が埋め込まれていれば誰も想像できず、実装者は意図しない挙動で混乱するだろう。

継承や多態性で気をつけること (ワイの経験則)

自分のサブクラスになり得るものにより処理を変えたりしちゃダメ。たとえば「果物」内で「もし自分がリンゴなら」「もし自分がバナナなら」という実装をしてはいけない。何故ならあくまでも「果物」の一種に「リンゴ」や「バナナ」があるのであって、「果物」自身は自分が何になり得るのかなど気にしちゃいないから。「自分は果物だ」としか知らないし、実際に「果物」として扱われる際にはそれにより挙動が変わってしまってはいけない。実装者はデバッグ時に意図しない挙動が発生し混乱するだろう。

OOPの良いところ

適切な名前付けで実装を担保するため、変化に強い。各オブジェクトが名付けされたとおりの小さな機能のみを持つため、その名前付けされた処理が正しく実装されている限りはオブジェクト間で正しく作用する。

OOPの残念なところ

理解するのがどうも難しいらしい。関わる開発者全員がOOPに精通していること、つまり適切な名前付けと責務分離が可能なことを前提としているため、誰か1人でも理解していない場合、暗黙の了解が壊れてしまう。何が起きるかというと、各 "気をつけること" で書いていることが発生し、バグの温床になる。

各オブジェクトがあって、それをどのように調理するか、という思考を全員が身に付けるまでは手続型言語の方が良いかもしれない。

おしまい

次は、FPについて学び直しメモ - 🍥shuma_yoshioka

DiscordにSpotifyの再生中楽曲を表示する`spoticord`をWindowsで使う

弟が使い方わからなくて困ってたので、他にもわからない人居て困ってるかもと思いメモ。

spoticordとは

nations/spoticord はDiscordのステータスにSpotifyで再生中の楽曲を表示できるコマンドラインツールです。

GitHub上で公開されています。

github.com

必要なもの

実行にあたっての必須要件は以下です。

  • Node.js 8 以降
  • npm 5 以降 (nodeに付属)

今回はWindowsへ導入するにあたり、さらに以下を追加します。

1. Git for Windowsをインストールする

Gitはソフトウェアのソースコードを管理するツールです。今回は主にspoticordのダウンロード / アップデートで内部的に使われます。
Git for Windows の Download から、32bit, 64bitなどお使いのOSに合ったインストーラをダウンロードします。

私の環境の場合、記事執筆時点ではGit-2.15.1.2-64-bit.exeが該当します。

f:id:S64:20171211192814p:plain

インストーラのダウンロードが完了したら、実行しインストールを進めてください。途中複数の選択肢が表示されますが、多くの場合はそのままで問題ありません。

2. GitHubへ会員登録する

この手順は必須ではありませんが、ダウンロード時の制約を受けにくくなる等セットアップをシンプルにできるため行うことを推奨します。

GitHub.comはGitで管理されたソフトウェアを管理・公開できるサービスです。ソフトウェアの利用のみであったり、ソースコードを公開する場合は無償で利用できます。
github.com にアクセスし、Sign upから登録してください。

3. GitHub Desktopをインストールする

GitHub Desktopは GitやGitHub.com上で公開されているソフトウェアを、Git初心者でも簡単に扱えるアプリケーションです。
GitHub Desktop からインストーラをダウンロードします。

f:id:S64:20171211194946p:plain

インストーラのダウンロードが完了したら、実行しインストールを進めます。インストールは自動で進められます。
自動でのインストールが完了すると、以下の画面が表示されます。Sign into GitHub.comをクリックし、さきほど作成したGitHub.comのアカウント情報を入力してください。
もしアカウントを作成しなかった場合、下部のSkip this stepを選択します。

f:id:S64:20171211195334p:plain

入力し進めていくとConfigure Gitなどステップが表示されますが、アカウント情報を入力した場合には原則そのままで問題ありません。最後のステップまで進めFinishをクリックしてください。

4. nodistをインストールする

node (Node.js) はJavaScriptを実行するツールです。nodistはWIndows上でのセットアップを簡略化し、複数バージョンの共存を可能にできるツールです。

nodistのReleasesから、最新のインストーラをダウンロードします。記事執筆時点では、NodistSetup-v0.8.8.exeです。
ダウンロードが完了したら、そのままインストールを進めてください。

5. nodistを使いnodeをインストールする

nodistのインストールが完了したら、nodistでインストールできるnodeの一覧を確認します。コマンドプロンプト (またはPowerShell) を起動し、以下のコマンドを実行します。

nodist dist

列挙されたバージョンの中から、インストールしたいバージョンを確認し、インストール用のコマンドを実行します。例えば今回の場合、spoticordの最低要件を満たしかつ最新の長期サポート版となる8.9.3が良いかもしれません。その場合、以下のコマンドを実行します。

nodist + 8.9.3

プロンプトが返ってきたら、システムで使うnodeのバージョンを設定します。今回は8.9.3を選んだため、以下のようになります。

nodist 8.9.3

Default global pacakge update dsuccessful.と表示されれば問題ありません。

6. spoticordのソースコードをcloneする

GitHub Desktopを開き、[File] -> [Clone repository...] をクリックします。表示されたダイアログのURLタブへ移動し、Repository URL欄に以下のURLを入力します。

git@github.com:nations/spoticord.git

f:id:S64:20171211201519p:plain

[Clone]をクリックし、ソースコードをcloneします。なお、ソースコードはLocal pathに書かれたディレクトリへダウンロードされるため、誤って削除しないようにします。
エラー無く正常に画面が切り替わった場合、cloneが完了しています。今後spoticordをアップデートする際は、この画面のFetch originから行います。

7. spoticordをセットアップする

GitHub Desktopの[Repository] -> [Open in Command Prompt] (またはPowerShell)をクリックすると、spoticordのソースコードが含まれたディレクトリでコマンドプロンプト (またはPowerShell) を開くことが出来ます。コマンドプロンプトを開いたら、以下のコマンドを実行します。

npm install

これにより、spoticordが利用するライブラリがダウンロードされます。
実行できない場合、コマンドプロンプトとGitHub Desktopを一度閉じ再度行ってください。

8. spoticordを起動する / 終了する

前述のセットアップから続けて (または今後同様の手順でコマンドプロンプトを開き) 、以下のコマンドを実行するとspoticordが起動します。

node app.js

終了する際は、一般的なコマンドラインアプリケーション同様に、このプロンプト上で [Ctrl] + [C] を入力すると終了します。

spoticordに貢献する

spoticordは無償で利用でき、さらにオープンソースソフトウェアとして提供されています。
今後も継続的にアップデートされるよう、利用者は以下のような方法で貢献できるでしょう。

  • バグ等を修正し、GitHub上でPull requestを送る
  • GitHub上でプロジェクトにStarを付ける

GitHubに登録している場合、プロジェクトにStarを付けることで開発者を応援することができます。

f:id:S64:20171211202959p:plain

クリックするだけなので、是非Starを付けて応援しましょう。

わからないことあったら聞いてください。

高校時代ハロワに行って、さんざんコケにされた話