条件分岐をどうやって関数型に?

これが展示会での間違い探しでした。

VBの元のコード

      Dim total As Decimal = 0

      For Each item In items
                If item.Category = "A" Then
                                    If item.Price > 1000 Then
                                    total += item.Price * 0.9
                                    Else
                                             total += item.Price
                                    End If
                End If
      Next
      If total > 50000 Then
                total = total * 0.95
      End If

      lblTotal.Text = total.ToString("C")

C#でリファクタリング

      var total = items
    		.Where(i => i.Category == "A")
    		.Sum(i => i.Price > 1000 ? i.Price * 0.9 : i.Price) * 0.95m;

      lblTotal.Text = $"{total:C}";

最後の、合計が50000以上だったら0.95を掛ける、というところの条件分岐が抜けていて、全部に0.95を書けてしまっています。

では、この条件分岐をどうやって関数型に持ち込むか?

一つ目は、拡張メソッド。

      public static TResult Pipe<T, TResult>(this T value, Func<T, TResult> f)
      => f(value);

とPipeメソッドを作ります。そうすると、x.Pipe(f) == f(x) となり、順番を後に続けて書くことができますね。

      var total = items
                  .Where(i => i.Category == "A")
                  .Sum(i => i.Price > 1000 ? i.Price * 0.9m : i.Price)
            .Pipe(sum => sum > 50000m ? sum * 0.95m : sum);

      lblTotal.Text = $"{total:C}";

たしかにロジカルで便利です。でも、ちょっと大げさではありますね。

C#にも、Haskellなど関数型言語でおなじみのパターンマッチングみたいなのがあって、switchを使うと、こう書けます。

      var total = items 
            .Where(i => i.Category == "A") 
            .Sum(i => i.Price > 1000 ? i.Price * 0.9m : i.Price) 
            switch
             { 
                  var s when s > 50000m => s * 0.95m, var s => s 
            };

       lblTotal.Text = $"{total:C}"; 

この場合はパターンマッチングの方が、パイプよりシンプルで綺麗かな。

拡張メソッドだとコードの見通しが悪くなって、逆にメンテが無茶苦茶になってしまうかも知れないです。

個人的には拡張メソッドを作って色々いじっているのは楽しいですが、火遊びしているとやけどしてしまいますね。

ところで数年前に「パイプを吸うとイケてるんじゃないか」という思いに憑りつかれ、始めたことがありました。

煙草の葉だけを詰めるだけだから、一番ロジカルな喫煙法だと思ったのですが、詰め方がものすごく難しい。

固く詰めすぎるとすぐに火が消えるし、柔らかすぎると燃えすぎる。葉をもみほぐして入れるとか、三層に入れるとか、色々とネットに解説されている方法を試したのですが、そのうち口の中をやけど。

もともと煙草も吸わず恰好だけだったので、すぐにやめてしまいました。

お問い合わせ
Contact

TOPへ戻る