読者です 読者をやめる 読者になる 読者になる

「Team Geek」 感想

感想

こちらも話題になっていたので読みました。

Team Geek ―Googleのギークたちはいかにしてチームを作るのか

Team Geek ―Googleのギークたちはいかにしてチームを作るのか

総評

0: 人によってはおすすめ

この本もいわゆるSoft Skillについて述べた本で、HRT(謙虚・尊敬・信頼)の考えをベースに様々な「チームが上手く行く」方法について述べています。
特にチームを率いるマネージャー(リーダー)に向けた本になっていると感じました。
もちろんそうでない人が読んで価値がないかというとそんなことはないのですが、チームを率いる人が読むべき事が多いように思います。

細々とした感想

HRT

謙虚・尊敬・信頼の頭文字を取ったもので、この本の根幹を成す考え方とされています。
とても重要な考えだと思うのですが、チームの全ての人がこの考えを持って動くには、チームの文化として強く根付いていないと難しいな、と感じました。
会社やチームの文化として根付いていれば新しく入ってきた人にも自然と受け入れられると思いますが、そうでないとなかなか難しいものです。
ミッションステートメントに含めるなど、考えを定着させる工夫が必要だと思いました。

マネージャーではなくリーダー

既存のマネージャーのイメージとは少し違いますが、あるべき理想的な姿だと思います。
「目標を明確にする」「メンターになる」「禅マスターになる」などなどいくつか述べられており、アンチパターンもあってお得です。
気になった方は是非読んでみてください。

有害な人に対処する

この本で一番考えさせられ、また実践が難しいポイントはここだと思いました。
あまりここに踏み込んだ本は無いように思いますが、どうしても発生してしまう問題です。
この本にかかれていることをベースに、強固な文化を作って上手くやっていくよう、日々頭を悩ませる必要があるかと思います。

まとめ

特にリーダーに向けて、強固なチームを作るためのノウハウが書かれています。
実践する場合、まずは如何にHRTを根付かせるかが鍵になるかと思います。
チームマネージメントとしてかなりレベルが高いですが、まずは自省から始めてみようかと思います。

Scala関数型デザイン&プログラミング 6章

概要

を読んだメモをまとめてゆきます。
今回は第6章。
Part 1の最後です。

各章のメモ

6章

この章では、状態を扱う方法が説明されています。
純粋関数なのにどうやって??という疑問がまず浮かびますが、やることは同じです。
リストでは結果を格納するためのリストを引数に入れていました。
状態を遷移させたければ、引数に状態を渡して、また返してあげれば大丈夫そうです。
ここでは乱数を生成する関数を例にとっています。

恒例の早速練習問題。

nextIntを使って、非負整数を返す関数を作れ。なお、nextIntがInt.MinValueを返すと対応する値がないので、特殊ケースとして対応すること。

負の場合は+1してあげることで、[0, Int.MaxValue]の全てが2回登場するようにできます。

  def nonNegativeInt(rng: RNG): (Int, RNG) = {
    val (i, next) = rng.nextInt
    if(i < 0)
      (-(i + 1), next)
    else
      (i, next)
  }

[0, 1)のDouble値を返す関数を作れ。

  def double(rng: RNG): (Double, RNG) = {
    val (i, next) = nonNegativeInt(rng)
    (i.toDouble / (Int.MaxValue.toDouble +1), next)
  }

様々な型を持ったタプルを作る関数を作れ。

  def intDouble(rng: RNG): ((Int,Double), RNG) = {
    val (i1, r1) = rng.nextInt
    val (d2, r2) = double(r1)
    ((i1, d2), r2)
  }

  def doubleInt(rng: RNG): ((Double,Int), RNG) = {
    val ((i1, d1), next) = intDouble(rng)
    ((d1, i1), next)
  }

  def double3(rng: RNG): ((Double,Double,Double), RNG) = {
    val (d1, r1) = double(rng)
    val (d2, r2) = double(r1)
    val (d3, r3) = double(r2)
    ((d1, d2, d3), r3)
  }

沢山書くのがめんどくさいな〜と思った時に以下の問題が出てきます。うまい。

ランダムな整数のリストを作成する関数を作れ。

  def ints(count: Int)(rng: RNG): (List[Int], RNG) = {
    @tailrec
    def loop(n: Int, acc: List[Int], r: RNG): (List[Int], RNG)  = {
      n match {
        case 0 => (acc, r)
        case _ =>
          val (i, nr) = r.nextInt
          loop(n-1, i :: acc, nr)
      }
    }
    loop(count, List.empty[Int], rng)
  }

ここで、RNGを受け取って値とnextRNGを返す型、Rand[+A]が導入されます。
よく出てくるので、まとめて扱うと便利そうですね。
このRandに対してもmapが定義されますので、これを使って書き換えろという練習問題が出ます。

mapを使ってdoubleをもう少し要領よく実装しなおせ。

なんだか随分曖昧な指示です。

// before
  def double(rng: RNG): (Double, RNG) = {
    val (i, next) = nonNegativeInt(rng)
    (i.toDouble / (Int.MaxValue.toDouble +1), next)
  }
// after
  def mapDouble: Rand[Double] =
    map(nonNegativeInt)(_.toDouble / (Int.MaxValue.toDouble +1))

随分すっきりしました。
共通パターンになりそうですね。

と思ったら、すぐさま「mapは高性能ではありません。map2を作りましょう」などと言われて心が折れます。
map2があれば、任意のRNG型アクションの結合ができるようになります。

map2を実装せよ。

  def map2[A,B,C](ra: Rand[A], rb: Rand[B])(f: (A, B) => C): Rand[C] =
    rng => {
      val (a, rng2) = ra(rng)
      val (b, rng3) = rb(rng2)
      (f(a, b), rng3)
    }

実装自体は簡単です。

[難問] 遷移のlistを1つの遷移にまとめるためのsequenceを実装せよ

_sequenceのように書いていましたが、Randを使ってない……。
解答を見ると、1行で書かれていました。Randがちゃんと使われています。なるほど。
unitも存在を忘れていました。Randの単位元なので確かに使いドコロはここだ……。

  def _sequence[A](fs: List[Rand[A]]): Rand[List[A]] = {
    rng => {
      fs.foldRight((List.empty[A], rng))((x, acc) => {
        val (a, r2) = x(acc._2)
        (a :: acc._1, r2)
      })
    }
  }
  def sequence[A](fs: List[Rand[A]]): Rand[List[A]] =
    fs.foldRight(unit(List[A]()))((x, acc) => map2(x, acc)(_ :: _))

  def intsSeq(count: Int): Rand[List[Int]] =
    sequence(List.fill(count)(int))

どんどんとrngが隠匿されていますね。
map / map2を使ってもうまく隠匿できないケースが紹介され、さらに強力なflatMapを作れという問題が出ます。

flatMapを実装し、nonNeegativeLessThanを実装せよ。

  def flatMap[A,B](f: Rand[A])(g: A => Rand[B]): Rand[B] = {
    rng => {
      val (a, r) = f(rng)
      g(a)(r)
    }
  }

  def nonNegativeLessThan(n: Int): Rand[Int] = {
    flatMap(nonNegativeInt)(i => {
      val mod = i % n
      if(i + (n-1) - mod >= 0)
        unit(mod)
      else
        nonNegativeLessThan(n)
    })
  }

受け渡しが自動化されました。
次の問題で、より関係性がわかりやすくなります。

flatMapを使ってmap, map2を再実装せよ。

  def map[A,B](s: Rand[A])(f: A => B): Rand[B] =
    flatMap(s)(i => unit(f(i)))
  def map2[A,B,C](ra: Rand[A], rb: Rand[B])(f: (A, B) => C): Rand[C] =
    flatMap(ra)(a => map(rb)(b => f(a, b)))

すっきりしました。

さて、このような状態の受け渡しを一般化すべく、State型が導入されます。
これを使って以下の問題が出されます。

unit, map, map2, flatMap, sequenceを一般化せよ。

一般化するだけ……とはいかず、状態遷移のイメージが難しいため答えをチラ見しながら書きました。

case class State[S,+A](run: S => (A, S)) {
  def map[B](f: A => B): State[S, B] =
    flatMap(a => State.unit(f(a)))

  def map2[B,C](sb: State[S, B])(f: (A, B) => C): State[S, C] =
    flatMap(a => sb.map(b => f(a,b)))

  def flatMap[B](f: A => State[S, B]): State[S, B] =
    State(s => {
      val (a, s2) = run(s)
      f(a).run(s2)
    }
  )
}

object State {
  type Rand[A] = State[RNG, A]

  def unit[S,A](a: A): State[S, A] = State(s => (a, s))
  def sequence[S,A](fs: List[State[S, A]]): State[S, List[A]] =
    fs.foldRight(unit[S, List[A]](List()))((x, acc) => x.map2(acc)(_ :: _))
}

この辺りはスラスラ書けるという状態には程遠いため、また振り返ることになりそうです。

今までやってきたことを鑑みると、結局は命令形プログラムと変わりありません。
各処理で状態を変更し、それを次に受け渡しているからですね。
というわけで、最後の問題が放り投げられます。

[難問] 自販機を作れ。

難しかったので答えを見ました。
抽象度が高いですが、まさしく命令形プログラミングを実現しています。
sequenceを使う辺りが解答だと少しややこしかったので、( )を使う記法にしています。
慣れてくるとスペースの方が読みやすいのかもしれません……。

object Candy {
  def update = (i: Input) => (s: Machine) =>
    (i, s) match {
      case (_, Machine(_, 0, _)) => s
      case (Coin, Machine(false, _, _)) => s
      case (Turn, Machine(true, _, _)) => s
      case (Coin, Machine(true, candy, coin)) =>
        Machine(false, candy, coin + 1)
      case (Turn, Machine(false, candy, coin)) =>
        Machine(true, candy - 1, coin)
    }

  def simulateMachine(inputs: List[Input]): State[Machine, (Int, Int)] = for {
    _ <- sequence(inputs.map((i: Input) => modify[Machine](s => update(i)(s))))
    s <- get
  } yield (s.coins, s.candies)
}

思ったこと

状態を持たない処理をずっと書いていましたが、遂に状態まで扱えるようになりました。
使いこなせれば、ステートフルな処理をステートレスに書けるという、極めて強力な記述ができそうです。
だんだんと真髄に近づいてきた感があります。

が、この本はまだ1/3程度しか終わっていません。
今回で遂にPart 1が終わり、次回からより本質的な話になっていくようです。
がんばります。

この章は消化不良なので、しばらく読んでからまた振り返りたいと思います。

「Soft Skills」 感想

感想

ひとしきり話題になっていたので読みました。

SOFT SKILLS ソフトウェア開発者の人生マニュアル

SOFT SKILLS ソフトウェア開発者の人生マニュアル

総評

+1: 結構おすすめ

道筋を考えるための本

プログラマーを対象とした技術書にカテゴライズされますが、一般的な技術書とは異なりキャリア、ひいては人生についての技術書となっています。

日々の仕事、キャリア形成から資産形成に至るまで、人生(特に社会人になってから)の多くについて触れています。
ここに書かれていることが全て実践できるようなことは無いかと思いますが、心にとめておき悩んだ時に読みかえすと進むべき方向が見えてくるかと思います。

細々とした感想

学び売り込み学ぶ

何かを学習し、それを人に教え、さらに学びを得る、というスパイラルのそれぞれを詳細に解説しています。
私もちょいちょいブログを書いていて意味があるのかと疑問に思うことがありますが、やはりアウトプットをすると整理されてよく吸収される感じがします。
積極的にアウトプットしよう!と思わせてくれます。
習慣化のテクニックについても述べられており、定期的に書き上げることの重要性を感じました。

資産形成

もともと投資は好きで色々とやっていますが、
「仮に50歳で引退すると考えると、一体いくらくらい貯金がいるのか?」
というような大きな話は考えたことがありませんでした。
ゴールからトップダウンして考えると、今何をすべきかが色々と考えられます。
アーリーリタイアをしたいとぼんやりとでも思っている方は、ぜひ考えてみてください。

KanbanFlow

ポモドーロテクニックは一時期使ってみていましたが、あまり効果を理解していなかったせいか、すぐ辞めてしまいました。
この本で解説されているように、計測・見積もりをするための方法だと考えると、大きく見方が変わってきます。
最近仕事中に使っていますが、なかなか好感触です。

まとめ

この本は、プログラマーとしての人生をうまく進めるための技術を解説しています。
ここに書かれていることが実践できるかは完全に自分次第で、人によっては何も残らないかもしれません。
とはいえ、ゴールを考えてみる上でも一度読んでおいて損はないと思います。

習慣化はなかなか大変ですが、意識をするだけで変わることもあると思います。
「ここはできる」「これはやらなくていい」という取捨選択をして、いいとこ取りをしてゆきたいです。

就活生でもないと、なかなか先の人生について考える機会も少ないと思いますが、
この本を契機に一度考えてみると、大きく人生が変わるかもしれません。

Scala関数型デザイン&プログラミング 〜5章

概要

を読んだメモをまとめてゆきます。
今回は第5章。

各章のメモ

5章

この章では非正格性、要は遅延評価周りを扱います。
この辺は凄く関数型っぽいですね。
実行効率にも大きく関与する部分ですし、現状あまり上手く使えていないので、しっかりと進めてゆきたいです。

Scalaの非正格関数

JSで死ぬほど出てくる↓の形で表されます。

def hoge(f: () => Int) = ... f()

hoge( () => 21 )

この形式はthunkと呼ばれ、redux-thunkなどでも名前が使われています。

ただ、毎回書くのが面倒なので、最初の空カッコは省略し、さらに呼び出しでもカッコが要らなくなります。
あら便利。

def hoge(f: => Int) = ... f

サンクから値を取り出す際、結果をキャッシュはしてくれないので、lazy付きの定数に放り込んであげる必要があります。

lazy val x = f

こうすると、xが初めて使われる際にfが評価され、さらに結果をキャッシュしてくれます。

Stream

ストリームの実装、キャッシュをするためのスマートコンストラクタが紹介された後、いつもの練習問題です。

StreamのtoListを作れ。

  def toList: List[A] = {
    this match {
      case Cons(h, t) => h() :: t().toList
      case _ => Nil
    }
  }

とりあえずこれで動きますが、末尾再帰でないので遅いです。
解答ではreverse付きのtailrec、mutable.ListBufferを使った答えも挙げられていました。

take, drop、takeWhileを作れ。

  def take(n: Int): Stream[A] =
    this match {
      case Cons(h, t) if n > 0 => cons(h(), t().take(n - 1))
      case _ => Empty
    }

  def drop(n: Int): Stream[A] =
    this match {
      case Cons(h, t) if n > 0 => t().drop(n - 1)
      case _ => this
    }

  def takeWhile(p: A => Boolean): Stream[A] =
    this match {
      case Cons(h, t) if p(h()) => cons(h(), t().takeWhile(p))
      case _ => Empty
    }

takeの解答ですが、n==1の時に余計なt()が呼び出されるのが無駄なので、n==1の時は分岐していました。
細かい所ですが、多くの箇所で使われるとなると、細やかな所に気を使わないといけないですね。


次に、existsの実装を通してStreamを活用する場面が書かれています。
見つかった時点で打ち切ることができ、foldRightもそのように実装することで最後まで評価をしなくて済みます。

ここでまたまた練習問題。

全て条件にマッチするかを判定するforAllを実装せよ。

  def forAll(p: A => Boolean): Boolean =
    foldRight(true)((a, b) => p(a) && b)

なんと全くStreamを意識せず書くことが出来ました。 まさに一般化されていますね。

foldRightを使ってtakeWhileを実装せよ。

  def takeWhile2(p: A => Boolean): Stream[A] =
    foldRight(Empty: Stream[A])((a, b) => if (p(a)) cons(a, b) else Empty)

foldRightを使ってheadOptionを実装せよ。

  def headOption: Option[A] = foldRight(None: Option[A])((a, _) => Some(a))

リストが空であればNoneなので、非常に素直な実装のこれだけで十分です。
2つ目の引数(b)が評価されていないので、先頭要素以降はループが発生せずちゃんとStreamを活かせています。

foldRightを使ってmap / filter / append / flatMap を実装せよ。

答え合わせで気づきましたが、Emptyの型指定はこのような形でも行えるようです。
楽ちん。

  def map[B](f: A => B): Stream[B] =
    foldRight(Empty: Stream[B])((a,b) => cons(f(a), b))

  def filter(f: A => Boolean): Stream[A] =
    foldRight(Empty: Stream[A])((a,b) => if(f(a)) cons(a, b) else b)

意気揚々と書いていると、appendでコンパイルエラーが取れません。
covarient / contravariant 辺りの問題ですが、いまいち分かっていなかったため他サイトのお世話になりました。

tkawachi.github.io

とてもわかりやすいです。
パッと使いこなせるかどうかはかなり怪しいですが、とりあえずvariance checkを回避できました。

  def append[B>:A](a: => Stream[B]): Stream[B] =
    foldRight(a)((c,d) => cons(c, d))

  def flatMap[B>:A](s: A => Stream[B]): Stream[B] =
    foldRight(Empty: Stream[B])((a,b) => s(a).append(b))

これらの関数を使ってStreamを処理すると、必要な部分だけ評価しており、余計なインスタンスが生成されることがありません。
下記のような処理をしても、とても効率よく処理してくれて重いリストも安心です。

st.map(---).filter(---)
st.filter(---).headOption

不完全なインスタンス化で処理を進行するため、ループの適用順自体が変わっているように見えます。
そのため、ストリームは「ファーストクラスループ」と呼ばれることもあるそうです。

filterで捨てられる、mapで生まれた要素もすぐGCの処理対象になるため、メモリにも優しいです。
また、より効率の良いストリーム計算については先の章で触れるそうです。


さて、ストリームの基礎を抑えたところで、よく例に挙げられる無限ストリームに触れています。
一瞬触れたら早速練習問題です。

指定された値の無限ストリームを生成する関数constantを作れ。

def constant[A](a: A): Stream[A] = cons(a, constant(a))

と簡単に書けるのですが、毎回consをするので少しもったいないです。
より良い方法として、lazyなオブジェクトを作って使い回す方法が挙げられています。

  def constant2[A](a: A): Stream[A] = {
    lazy val v: Stream[A] = Cons(() => a, () => v)
    v
  }

n, n+1, n+2...の無限ストリームを作る関数fromを作れ。

  def from(n: Int): Stream[Int] = cons(n, from(n+1))

フィボナッチ数列ストリームを作る関数fibsを作れ。

  def fibs(): Stream[Int] = {
    def loop(i1: Int, i2: Int): Stream[Int] = cons(i2, loop(i1 + i2, i1))
    loop(1, 0)
  }

よくある例ですね。

より汎用的なストリーム生成関数unfoldを作れ。また、それを使ってfibs / from / constant / onesを作れ。

  def u_fibs(): Stream[Int] =
    unfold((0, 1))(a => Some(a._1, (a._2, a._1 + a._2)))
  def u_fibs2(): Stream[Int] =
    unfold((0, 1))(a => {
      a match {
        case (i1, i2) => Some(i1, (i2, i1 + i2))
      }
    })
  def u_fibs3(): Stream[Int] =
    unfold((0, 1)) { case (i1, i2) => Some(i1, (i2, i1 + i2)) }

  def u_from(n: Int): Stream[Int] =
    unfold(n)(s => Some(s, s + 1))

  def u_constant(n: Int): Stream[Int] =
    unfold(n)(s => Some(s, s))

  def u_ones: Stream[Int] =
    unfold(1)(s => Some(s, s))

fibsは3つありますが、どれも同じです。
変数分離時にmatchを省略して書く書き方を使うと、とても楽です。

unfoldを使ってmap / take / takeWhile / zipWith / zipAllを作れ。

  def u_take(n: Int): Stream[A] =
    unfold((this, n)) {
      case (Cons(h, t), i) if i > 0 => Some(h(), (t(), i-1))
      case _ => None
    }
  def u_takeWhile(f: A => Boolean): Stream[A] =
    unfold(this) {
      case Cons(h, t) if f(h()) => Some(h(), t())
      case _ => None
    }
  def zipWith[B, C](l2: Stream[B])(f: (A, B) => C): Stream[C] = {
    unfold((this, l2)) {
      case (Cons(h1, t1), Cons(h2, t2)) => Some(f(h1(), h2()), (t1(), t2()))
      case _ => None
    }
  }
  //  def zipAll[B, C>:A](l2: Stream[B])(v1: A, v2: B): Stream[(A,B)] = {
  //    unfold((this, l2)) {
  //      case (Empty, Cons(h2, t2)) => Some((v1, h2()), (Empty, t2()))
  //      case (Cons(h1, t1), Empty) => Some((h1(),v2), (t1(), Empty))
  //      case (Cons(h1, t1), Cons(h2, t2)) => Some((h1(), h2()) -> (t1(), t2()))
  //      case _ => None}
  //  }
  def zipAll[B](l2: Stream[B]): Stream[(Option[A], Option[B])] = {
    unfold((this, l2)) {
      case (Empty, Cons(h2, t2)) => Some((None, Some(h2())), (Empty, t2()))
      case (Cons(h1, t1), Empty) => Some((Some(h1()), None), (t1(), Empty))
      case (Cons(h1, t1), Cons(h2, t2)) => Some((Some(h1()), Some(h2())), (t1(), t2()))
      case _ => None
    }
  }

解答を見るとzipAllで(Option[A], Option[B])を返していますが、Noneにしろと指定されているので従います。

startWithを作れ。

  def startsWith[B](s: Stream[B]): Boolean =
    zipAll(s).takeWhile(_._2.isDefined).forAll({
        case (Some(a), Some(b)) => a == b
        case _ => false
    })

2つのストリームをペアにしてから、プレフィックス分だけ取り出し、全ての要素が一致するかを判定しました。

unfoldを使ってtailsを実装せよ。

普通のtailのように、ちょっとずつ削っていけば良さそうです。
空ストリームは明示的にくっつけてあげないといけません。

  def tails: Stream[Stream[A]] =
    unfold(this) {
      case Empty => None
      case s => Some(s, s.drop(1))
    }.append(Stream(empty))

tailsを一般化してscanRight関数を作れ。

分からないので解答を確認。
メモとしての変数をくっつけてfoldRightで生成しているようです。
tailsはunfoldを使って書いたのに、一般化したこちらではunfoldが使えないというのが何だか不思議です。
やってることは同じに見えるのですが……。

思ったこと

なんとか終わりました。
軽そうに見えて重たい商です。
使いこなす所まで至るにはまだまだ修行が必要そうですが、知ってるか知らないかでは応用範囲が大きく変わってきそうです。
効率的な処理を行う上では必須ですし、記述と評価を切り離すことでモジュール性も上がると述べられています。
後の章でも例が沢山出てくるらしく、まだまだ奥が深そうだ……。

次の章では「状態をいかに扱うか」という点に触れます。

陸でマイルを稼ぐ方法 調査まとめ 2016/10

マイル

マイルについて色々調べたり、メインのクレカを変えたりしたので、備忘録としてさっくり残しておきます。
ちなみに、飛行機に乗りたくない人にとっては全くもって無益な記事です。

詳細はあまり書いていないので、興味がある方は調べてみてください。

参考にしたサイト

先達の上級マイラー様たちが様々な記事をまとめているので、ぜひ色々探してみてください。
ここに書ききれないほどありますが、いくつか貼っておきます。

ANAとマイルのパパじゃない?

理系マイラーとSFC修行

すけすけのマイル乞食

背景

  • 何となくマイルをためたくなった
  • 漢方カードの還元率が1.5%に落ちた
  • マイルを貯めるのに、やたら複雑な方法を使わないとお得にならなかった

前提

以下の前提に基づいた記事です。

  • 旅行・飛行機が好き
  • エコノミークラスでもいいが、あわよくばビジネス・ファーストに乗りたい
  • 1マイル = 2円

1マイルは2円で考えられる事が多いですが、格安航空券だったりちょっとイマイチな航空会社と比較すると1円程度にまで落ちることが多いように思います。
しかしANAの最安チケットと比べると2倍、夢のビジネス・ファーストクラスでは安いチケットが無いので、マイルの価値は10倍以上になります。
その辺りを考慮して、一般に語られる2円にしています。

達成できること

  • 日々のクレカ使用で、ANAマイルとして還元率1.5〜1.7%(金額にすれば3%程度は狙える)
  • (ポイントサイトで貯めれば)効率よくANAマイルに変換できるルートの確保

必要なもの

必要なものの一覧です。
なお、下記のリンクから登録してもらえると私が少し得して嬉しいです。
なぜこれらが必要なのかは後述しています。

必須

クレカだけ

クレカだけ変えたい人用です。

マイルを貯めたい

他にもポイントサイトは山のようにありますが、とりあえずハピタスとPointTownで案件を探して、まだ管理できそうなら他に手を広げるとよいかと思います。
沢山あると分散しますし、何より管理で疲れます。

日頃やること

  1. ネットで買うときはポイントサイト経由で!
  2. 毎月、リボ払いの設定金額をいじる

例えばamazonはポイントアップモール経由でANAマイルが貯められます。
JALマイレージバンク経由のJALマイルの方がお得ですが、ANAに固めたいのでこちら。
大して労力もなく(サイトを経由するだけ)マイルがちょっとたまるのでオススメ。

2番はクレカの還元率を上げる方法です。後述。

他、やったら貯まること

  • とにかくポイントサイトでポイントを貯める
  • 他、お得なイベントがあれば参加する

多くのマイラーさん達が記事にして挙げてくれているので、それをチェックして気が向いたら参加します。
たまにアメックスが祭りをやるようなので、大きなイベントは逃さないようにしたいところです。

マイルが貯まる仕組み

ここからは原理的なところです。

ルート

ANAマイルまでのルートは大別して以下の2つです。

  1. ポイントサイト -> .money (or PEX) -> メトロポイント (Tポイント) -> ANAマイル
  2. 三井住友のポイント -> Gポイント -> メトロポイント -> ANAマイル

1番はポイントサイトからの流入です。
メトロポイントを使うルートは変換ロスが非常に少なく、ソラチカルートと呼ばれ愛されているようです。
2番はクレカのポイントを最大限活かすルートです。

なるべくロス無くANAマイルにすることで効率を上げるのがキモです。

クレカについて

マイルを貯めるのにクレカ決済は特に役立ちません。
何故なら、ポイントサイトの還元率が非常に高いからです。
とは言え、日頃のお買い物もお得にする術はあり、マイルとして1.4〜1.7%程度の還元率を発生させることができます。

この還元率にするためには、リボ払いをしないといけません。
その手数料を最小限にするために、毎月設定をいじる必要があります。
方法は探すと沢山出てきます。
ちょっと面倒ですが、トレードオフですね。

なお、年会費は15000円となっていますが、Web明細にしたりすると9500円程度に下がります。
普段使う金額と勘案して、損益分岐点を考えましょう。

なお、最近では↓のような手法もあるらしいですが、LINE Payが新しく動きが読めないので様子見です。

「LINE Payカード」のチャージで一番得するのは毎週火・土曜日に「ファミマTカード」を使うこと!還元率が2%+1%=3%にアップする裏ワザ公開!|おすすめクレジットカード比較|ザイ・オンライン

まとめ

上記だけであれば、そこまで手間なく管理できそうです。
ポイントサイトは貯めたくなったり大きなイベントの時だけ利用すれば手間はかかりません。
日頃の買い物、食事などでもし利用できる場面があれば使ってみるとよいかと思います。

それにしても複雑な世の中になったものです(しみじみ)

Scala関数型デザイン&プログラミング 〜4章

概要

を読んだメモをまとめてゆきます。
今回は第4章。

各章のメモ

4章

この章では、エラー処理について触れています。
まず、例外が参照透過でないこと、型安全でないこと、mapなどの汎用性の高い関数との相性が最悪であることが述べられています。
代わりにnullや特殊な値を返す方法、呼び出し元でエラー処理関数を引数として渡す方法などを挙げて問題点を列挙しています。

Option

これらに代わるアプローチとして、Option型が紹介されます。
ここで早速練習問題が出ます。

Option型に対するmap, flatMap, getOrElse, orElse, filterを実装せよ。

  def map[B](f: A => B): Option[B] =
    this match {
      case None => None
      case Some(a) => Some(f(a))
    }

  def getOrElse[B>:A](default: => B): B =
    this match {
      case None => default
      case Some(a) => a
    }

  def flatMap[B](f: A => Option[B]): Option[B] =
    this.map(f).getOrElse(None)

  def orElse[B>:A](ob: => Option[B]): Option[B] =
    this.map(Some(_)).getOrElse(ob)

  def filter(f: A => Boolean): Option[A] =
    this.flatMap(a => if(f(a)) Some(a) else None)

Optionがネストしたりして少しややこしいですが、必要な処理と必要な型を考えるとそこそこスムーズに書くことが出来ました。

flatMapをベースとして、variance関数を実装せよ。

エラー処理を気にせず書くと出来上がりです。

  def variance(xs: Seq[Double]): Option[Double] =
    mean(xs).flatMap(m => mean(xs.map(a => math.pow(a - m, 2))))

便利で安全なOptionですが、今までの関数を全て対応するよう書き換えないといけない?という不安があがってきます。
そんな時のためにliftという考えを使って、普通の関数をOption対応させることが可能です。

2項関数を使ってOption型を2つ受け取るmap2を作れ。

  def map2[A,B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] =
    a.flatMap(sa => b.map(sb => f(sa, sb)))

配列に対しNoneを1つでも含む場合はNoneを返し、それ以外は全てのSomeを返すsequence関数を作れ。

foldRightでリストを作る途中で、先ほどのmap2を使えば良さそうです。

  def sequence[A](a: List[Option[A]]): Option[List[A]] =
    a.foldRight(Some(Nil): Option[List[A]]) ((x, y) => map2(x, y)(_ :: _))

1度しか走査を行わない、sequenceを総称したtraverse関数を作れ。

上記sequenceととほとんど同じですね。

  def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] =
    a.foldRight(Some(Nil): Option[List[B]]) ((x, y) => map2(f(x), y)(_ :: _))

Either

Optionはエラーが起きたことを知らせてくれますが、追加情報を与えてはくれません。
そこで、Eitherデータ型を使って情報を付与したエラーを表現します。

Eitherは、Right(正常値)とLeft(エラー値)の直和からなるデータ型です。
どちらかに値が入るわけですね。

またまた早速練習問題です。

Right値を操作するmap/flatMap/orElse/map2を作れ。

  def map[B](f: A => B): Either[E, B] =
    this match {
      case Right(v) => Right(f(v))
      case Left(v) => Left(v)
    }

  def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] =
    this match {
      case Right(v) => f(v)
      case Left(v) => Left(v)
    }

  def orElse[EE >: E, B >: A](b: => Either[EE, B]): Either[EE, B] =
    this match {
      case Right(v) => Right(v)
      case Left(_) => b
    }

  def map2[EE >: E, B, C](b: Either[EE, B])(f: (A, B) => C): Either[EE, C] =
    flatMap(a => b.map(f(a, _)))

flatMapとmapの組み合わせは、syntax sugarが用意されていてforで書けます。
まだ定着していないのでflatMap/mapで書いてしまいましたが、forにも慣れていきたいです。

sequence, traverseを作れ。

  def sequence[E,A](es: List[Either[E,A]]): Either[E,List[A]] =
    es.foldRight(Right(Nil): Either[E, List[A]])((a, b) => a.map2(b)(_ :: _))

  def traverse[E,A,B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] =
    es.foldRight(Right(Nil): Either[E, List[B]])((a, b) => f(a).map2(b)(_ :: _))

ほとんど同じなので、sequenceはtraverseを使って書くこともできますね。
traverseのAがEither[E, B]になればよいです。

map2でつなげた際、複数のエラーが起きていても1つしか報告できない。何を変更すればよいか。

Eitherは1つしかエラーを持てないので、Leftをリストにしてあげれば複数持てそうです。
エラーが発生するたびにリストに追加してゆくイメージです。


ここで4章は終了です。
3章の怒涛の練習問題の後だと、多少物足りない感はあります。
が、まだまだ先は長いですので、各章はこれくらいの長さが良かったりします。

次章では非性格の話です。
徐々に深いところに潜っている感じがします。

思ったこと

本章ではエラー処理を扱いましたが、実際のプログラムでも特に役立つ章だと思いました。
例外を使えば一瞬で片付きますが、フローが乱れたり型安全が崩れたり処理し忘れたりと、デメリットも大きいです。
エラーも値として後でまとめて処理する、純粋性を保つための工夫が詰め込まれていると感じました。

Scala関数型デザイン&プログラミング 〜3章

概要

を読んだメモをまとめてゆきます。
今回は第3章。

各章のメモ

3章

純粋関数では副作用が無いわけですが、どうやってデータを更新するの?というのが主な章です。

早速Listの定義が現れて面食らいますが、読み解くのはそこまで難しくありませんでした。
単方向リストの説明も行われ、パターンマッチの説明があります。
matchはやっぱり便利ですね。

ここでパターンマッチの簡単なクイズがあります(略)。

次に、データが変更されないことを利用して、データの共通化をして節約してるよ、という話が入ります。
ここから怒涛の練習問題です。
大量に出てきます。詰込型プログラミングです。

Listに対するtail, setHead, drop, dropWhile, initを実装せよ。エラー処理は各自考えよ。

リストの定義などはgithubに上げられていますので活用します。

github.com

エラー処理は色々と方法がありそうですが、とりあえずNilを返すことにしました。
本家ScalaのListでは例外を吐いていましたが、とりあえず実装を優先します。

  def tail[A](l: List[A]): List[A] = l match {
    case Cons(_, xs) => xs
    case Nil => Nil
  }

  def setHead[A](l: List[A], h: A): List[A] = l match {
    case Cons(_, xs) => Cons(h, xs)
    case Nil => Nil
  }

  def drop[A](l: List[A], n: Int): List[A] = {
    if(n <= 0)
      l
    else {
      l match {
        case i =>
          l match {
            case Cons(_, xs) => drop(xs, n - 1)
            case _ => l
          }
      }
    }
  }

  def dropWhile[A](l: List[A], f: A => Boolean): List[A] = l match {
    case Cons(x, xs) if f(x) => dropWhile(xs, f)
    case _ => l
  }

  def init[A](l: List[A]): List[A] = l match {
    case Cons(_, Nil) => Nil
    case Cons(x, xs) => Cons(x, init(xs))
    case Nil => Nil
  }

↓はテスト用のコードです。

object Main extends App {
  val x = List(1,2,3,4,5)
  val y = List()

  println("  # ex 3.2")
  println(List.tail(x))
  println(List.tail(y))

  println("  # ex 3.3")
  println(List.setHead(x, 10))
  println(List.setHead(y, 10))

  println("  # ex 3.4")
  println(List.drop(x, 1))
  println(List.drop(x, 4))
  println(List.drop(x, 10))
  println(List.drop(y, 1))

  println("  # ex 3.5")
  println(List.dropWhile(x, (a:Int)=> a < 4))
  println(List.dropWhile(x, (a:Int)=> a > 4))
  println(List.dropWhile(x, (a:Int)=> a < 100))

  println("  # ex 3.6")
  println(List.init(x))
  println(List.init(y))
}

dropだけネストしてて不格好だなぁ……。

豆知識として、dropWhileの型推論を改善する方法が紹介されています。
カリー化して2つ目の引数を推測できるようにしています。なるほど。

さて、既に定義されていたsumとproductを一般化するため、foldRightが出てきました。
Scalaを触り始めて最初にこいつを見た時は、全く意味がわからなかったことを覚えています。

さて、またしても練習問題です。

foldRightでのproductは0.0を検出した瞬間に、直ちに再帰を中止して0.0を返せるか?

質問されてしまいました。こういうパターンもあるのね。
独自のproductであれば0.0の要素まで来た時に続く再帰をさせず0.0を返すことはできますが、そこまでの要素は展開されてしまい計算するしかありません。
foldRightだとfの中でそのような処理を行えますが、fが評価されるのは全て展開された後です。
というわけで「直ちに0.0を返せるか」という問はNoだと言えます。

Nil及びCons自体をfoldRightに渡すとどうなるか?データコンストラクタとの関係は?

なんとも曖昧な質問ではありますが、畳み込みでListのコンストラクタができる、ということだと思います。
2要素をくっつけるのも演算として捉えられ、それを繰り返すことで単方向リストも作れるよ、というまさに抽象的な考え方です。

foldRightを使ってリストの長さを求めよ。

  def length[A](l: List[A]): Int = foldRight(l, 0)((_, y) => y + 1)

foldRightは末尾再帰でないため、スタックセーフでない。末尾再帰なfoldLeftを実装せよ。

  @tailrec
  def foldLeft[A, B](l: List[A], z: B)(f: (B, A) => B): B = l match {
    case Nil => z
    case Cons(x, xs) => foldLeft(xs, f(z, x))(f)
  }

foldLeftがさっくり出てきましたね。
末尾再帰でないから〜という方向から紹介するのは何となく目新しいです。

foldLeftを使って、sum/product/lengthを実装せよ。

  def flSum(l: List[Int]) = foldLeft(l, 0)(_ + _)
  def flProduct(l: List[Double]) = foldLeft(l, 1.0)(_ * _)
  def flLength[A](l: List[A]): Int = foldLeft(l, 0)((x,y) => x + 1)

reverseを実装せよ。

  def reverse[A](l: List[A]): List[A] = foldLeft(l, Nil: List[A])((x,y) => Cons(y, x))

foldRightでコンストラクトすると普通の順でしたが、foldLeftを使うと逆順になりますね。

[難問] foldRightを使ってfoldLeftを記述できるか?またその逆は?

難問マークは、なんか辛い問題に付けられているらしいです。
foldRightはf(a1, f(a2, f(a3, init)))、foldLeftはf( f( f(init, a1), a2), a3)となっていることを考えると、リストをひっくり返して適用すれば同じようなことができそうです。
ただ、fの適用順も違いますので、引数の順をひっくり返したf2を用意しました。

  def foldLeft2[A, B](l: List[A], z: B)(f: (B, A) => B): B = {
    val f2: (A, B) => B = (a: A, b: B) => f(b, a)
    foldRight(reverse(l), z)(f2)
  }
  def foldRight2[A, B](as: List[A], z: B)(f: (A, B) => B): B = {
    val f2: (B, A) => B = (b: B, a: A) => f(a, b)
    foldLeft(reverse(as), z)(f2)
  }

テスト用コードです。

  println(List.foldRight(List(1,2,3,4), 0)(_ - _))
  println(List.foldRight2(List(1,2,3,4), 0)(_ - _))
  println(List.foldLeft(List(1,2,3,4), 0)(_ - _))
  println(List.foldLeft2(List(1,2,3,4), 0)(_ - _))

上手く行ってそうです。ただ、reverseでfoldLeftを使っているので、foldLeft2をfoldLeftとして使うと無限ループしてしまいました。

解説を読むと、何ともいかついコードが現れました。

  def foldRightViaFoldLeft_1[A,B](l: List[A], z: B)(f: (A,B) => B): B =
    foldLeft(l, (b:B) => b)((g,a) => b => g(f(a,b)))(z)

ぱっと見では理解できませんが、具体的なListなどを放り込んであげると見えてきました。
うまーく関数を渡すことで、処理順を変えています。
これがパッと書けるとステキ。

foldRight or foldLeftを使ってappendを実装せよ。

  def flAppend[A](a1: List[A], a2: List[A]): List[A] = foldRight(a1, a2)(Cons(_, _))

うーん、スッキリ。

[難問] 既に定義した関数を使って、複数のリストからなるリストを1つのリストとして連結する関数を実装せよ。

appendを使って各要素をListとみなしてくっつけていけば出来そうです。

  def concat[A](l: List[List[A]]): List[A] = foldLeft(l, Nil: List[A])(append(_, _))

IntelliJの教えに従い、appendは以下のように簡単に呼び出せます。

 foldLeft(l, Nil: List[A])(append)

各要素に1を足したリストを返す関数を実装せよ。

 def plusOne(l: List[Int]): List[Int] = foldRight(l, Nil: List[Int])((x,y) => Cons(x + 1, y))

List[Int]をList[Double]に変換する関数を実装せよ。

  def doubleToString(l: List[Double]): List[String] = foldRight(l, Nil: List[String])((x,y) => Cons(x.toString, y))

mapを作れ。

  def map[A, B](l: List[A])(f: A => B): List[B] = foldRight(l, Nil: List[B])((x,y) => Cons(f(x), y))

あのmapがこんなにも簡単に!

filter関数を記述せよ。

  def filter[A](as: List[A])(f: A => Boolean): List[A] = {
    foldRight(as, Nil: List[A])((x,y) => {
      if(f(x))
        Cons(x, y)
      else
        y
    })
  }

ifが出てきて若干不格好です。

flatMapを実装せよ。

  def flatMap[A, B](as: List[A])(f: A => List[B]): List[B] = concat(map(as)(f))

事前に作成した関数が活きました。

flatMapを使ってfilterを実装せよ。

条件を満たさない場合は空配列を返してあげることで、flattenした際に勝手に消えてくれそうです。
というわけで、受け取った関数を少しいじってflatMapに渡します。

  def ffilter[A](as: List[A])(f: A => Boolean): List[A] = {
    val t = (a: A) => if(f(a)) Cons(a, Nil) else Nil
    flatMap(as)(t)
  }

リストを2つ受け取り、対応する要素同士を足しあわせたリストを返す関数を実装せよ。

  def plus(l1: List[Int], l2: List[Int]): List[Int] = {
    def loop(xs1: List[Int], xs2: List[Int]): List[Int] = {
      xs1 match {
        case Cons(x, xs) =>
          xs2 match {
            case Cons(y, ys) => Cons(x + y, loop(xs, ys))
            case Nil => Nil
          }
        case Nil => Nil
      }
    }
    loop(l1, l2)
  }

なんだか不格好ですね。
matchが入れ子になっていてどうにもみにくい……。

↑を一般化したzipWithを作れ。

  def zipWith[A, B, C](l1: List[A], l2: List[B])(f: (A,B) => C): List[C] = {
    def loop(xs1: List[A], xs2: List[B]): List[C] = {
      xs1 match {
        case Cons(x, xs) =>
          xs2 match {
            case Cons(y, ys) => Cons(f(x, y), loop(xs, ys))
            case Nil => Nil
          }
        case Nil => Nil
      }
    }
    loop(l1, l2)
  }

解説を見ると、2つのListをまとめてmatchで判定しています。なるほど、綺麗。

  def plus2(l1: List[Int], l2: List[Int]): List[Int] = {
    (l1, l2) match {
      case (Nil, _) => Nil
      case (_, Nil) => Nil
      case (Cons(x,xs), Cons(y,ys)) => Cons(x + y, plus2(xs, ys))
    }
  }
  def zipWith2[A](l1: List[A], l2: List[A])(f: (A,A) => A): List[A] = {
    (l1, l2) match {
      case (Nil, _) => Nil
      case (_, Nil) => Nil
      case (Cons(x,xs), Cons(y,ys)) => Cons(f(x, y), zipWith2(xs, ys)(f))
    }
  }

[難問] List (sup)の一部にあるList (sub)が含まれているかを判定する、hasSubsequenceを作れ。効率的なのは難しいかもしれない。5章でもう一度取り上げる。

手前から順番にsub.length個取り出すのを試してみると実現できそうです。
まずはtakeを実装し、それを利用して作りました。

  def take[A](l: List[A], n: Int): List[A] = {
    n match {
      case 0 => Nil
      case _ =>
        l match {
          case Cons(x, xs) => Cons(x, take(xs, n-1))
          case Nil => Nil
        }
    }
  }

  def hasSubsequence[A](sup: List[A], sub: List[A]): Boolean = {
    // 長さnの配列を前から順に取り出して比較
    def make(l: List[A], n: Int): Boolean = {
      if(take(l, n) == sub)
        true
      else {
        l match {
          case Nil => false
          case Cons(x, xs) => make(xs, n)
        }
      }
    }

    make(sup, length(sub))
  }

挙動は大丈夫そうですが、相変わらず若干不格好です。
とりあえず答えがどうなっているか見ると、startsWithをまず作って利用しています。
とにかく前方から一致を見てゆく方法です。確かにこれで十分だ。

シンプルな上に両方末尾再帰になっていて何とも凄いです。
が、著者的にも「正しい挙動をするかわかりにくい」そうです。
5章で再度取り上げる胸熱展開らしいので、そこまで待機……。


ここまで大量の練習問題が出てきて満身創痍ですが、さらに新しいデータ構造が出てきます。 おなじみツリーです。
代数的データ型の説明があり、Treeの実装、及び練習問題が出てきます。

2分木のノード数を数えるsize、最大の要素を返すmaximum、二分木のルートから任意のノードへの最長距離を返す関数depth、二分木の各要素を変換するmapを実装せよ。

  def size[A](t: Tree[A]): Int = {
    t match {
      case Leaf(a) => 1
      case Branch(l, r) => size(l) + size(r)
    }
  }

  def maximum(t: Tree[Int]): Int = {
    t match {
      case Leaf(a) => a
      case Branch(l, r) => maximum(l).max(maximum(r))
    }
  }

  def depth[A](t: Tree[A]): Int = {
    t match {
      case Leaf(a) => 1
      case Branch(l, r) => size(l).max(size(r))
    }
  }

  def map[A, B](t: Tree[A])(f: A => B): Tree[B] = {
    t match {
      case Leaf(a) => Leaf(f(a))
      case Branch(l, r) => Branch(map(l)(f), map(r)(f))
    }
  }

上記関数を一般化したfoldを実装せよ。また、それを使って上記関数を再実装せよ。

  def fsize[A](t: Tree[A]): Int = fold(t)(_ => 1)(_ + _)
  def fmaximum(t: Tree[Int]): Int = fold(t)((a:Int) => a)(_ max _)
  def fdepth[A](t: Tree[A]): Int = fold(t)(_ => Tree.fsize(t))(_ max _)
  def fmap[A, B](t: Tree[A])(f: A => B): Tree[B] = 
    fold(t)(a => Leaf(f(a)): Tree[B])(Branch(_,_))

いやー、短い実装だ。
型にガイドされながら書けます。

ようやくおしまいです。
一般化のスキルを磨いてね!という修行を課して本章は締められています。

思ったこと

大量の練習問題がありましたが、それぞれ思ったより短く書けてびっくりしました。
沢山解いていると、何となく思考方法が見えてきたような気がしないでもないです。

木の走査など、「めんどくさい……」となる処理でもシンプルに書けるのは、一度体験する価値があると思います。

次章はエラー処理で、普段のプログラミングでも困りがちな箇所にフォーカスしてゆくそうです。