Functional Programming w/ C# LINQ

Python は好きになれない。

Python の長所と言われているものは、プログラミング初心者相手のごまかしであるか、あるいは C などの古い言語に対するアドバンテージであり、ライバルとなる他のモダン言語に対するものではない。オフサイドルールも弊害が大きくて嫌いであるが、一番の嫌いな点は、列挙 (Enumeration) の論理構成が直感に反し、思考がいちいち妨げられることだ。思考の順序と異なるために読みにくいし、書くときはカーソルを右に左に大きく動かさなければならない。

列挙の扱い方を C# (LINQ メソッドチェーン) と Python とで比較してみる。

1 から 10 までの整数のうち、偶数には 'Even', 奇数には 'Odd' を付してタプルを返す
  // C#
  Enumerable.Range(1, 10)
    .Select( x => x % 2 == 0 ? (x, "Even") : (x, "Odd") );

偶数なら 'Even'、そうでないなら 'Odd'。条件式 → 結果1 → 結果2 と並ぶ。

  # Python
  [ (x, 'Even') if x % 2 == 0 else (x, 'Odd') for x in range(1, 11) ]

'Even' を返すよ、偶数なら、そうでないなら 'Odd'。結果1 → 条件式 → 結果2と並ぶのは非常にみづらい。
なぜ、条件式を結果ではさむのか。

1 から 10 までの整数のうち、偶数のみを2乗して返す
  // C#
  Enumarable.Range(1, 10)
    .Where( x => x % 2 == 0 )
      .Select( x => x * x );

1 から 10 を対象として、偶数に絞り、2乗する。お題の指示の順序の通り。

  # Python
  [ x * x for x in range(1, 11) if x % 2 == 0 ]

2乗を返すよ、1 から 10 を対象として、ただし偶数のみね。うーん。なぜ、列挙を map と filter ではさむのか。
2乗を返すよ、から始めるならせめて、2乗を返すよ (map)、偶数のみね (filter)、1 から 10 を対象として (enumerate)、にならないものか。

ジャグ配列をフラット化する、フラット化して各要素を2乗する
  // C#
  var arr = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ];

  arr.SelectMany( i => i );                      // フラット化
  arr.SelectMany( i => i.Select( j => j * j ) ); // フラット化して各要素を2乗

SelectMany で要素を掴む i が IEnumerable<int> 型であることを意識すれば難しくない。

  # Python
  arr = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]

  [     j for i in arr for j in i ] # フラット化
  [ j * j for i in arr for j in i ] # フラット化して各要素を2乗

[ j for j in i for i in arr ] と書きたいところ。なぜ arr と要素変数 i, j がこのような並びになるのか。


Python は列挙を扱える点において古い言語よりアドバンテージがあるが、その文法は

  • 列挙走査 (for) は後置する (ただし、複数の for はネストの上位から下位に向かって並べる)
  • if は後置する (if - else 三項演算子は後置 if の語順を踏襲した発展形)

というようになっている。

Perl の if 修飾子を参考にしたのか、全体的に SQL の語順に似ているが ... と想像をめぐらすが、いずれにせよちぐはぐ感は否めない。列挙に対する map, filter, reduce 操作をスムーズに記述するだけの統合感・先進性には欠けており、思考を乱す順序でしかロジックを記述できない言語であると思う。データサイエンティストに好まれるというのが信じられない。