p_tan's blog

勉強日記です。ツッコミ大歓迎

なぜC#よりもF#なのか 2017 #fsharp

以前このような記事を書きました。

なぜC#よりもF#なのか - p_tan's blog

「F# C# 比較」とかでググると比較的上位に表示されるようで、毎日少しずつアクセスがあるようです。 さすがに6年前の記事で内容が古いので、現状での F# を見てみたいと思います。 なお、今でもまだC#はほぼ触っておらず、F#は自分ではあまり書いていなくて職場での導入支援を行っているだけなので、間違っていたら指摘をいただけると嬉しいです。

2011年からの変化

C#と比較したF#の利点と欠点

F#がC#よりも優れている点
  • より短く安全な記述が可能
    • デフォルトで値による等価性をもつレコード型
    • 型のグルーピングのための判別共用体
    • C#より簡単に関数を値として扱える。引数の部分適用も容易
    • パイプ演算子による流れるような処理の記述
    • 強力なパターンマッチ
      • 判別共用体、アクティブパターン
      • コンパイラによる分岐の考慮漏れや到達しないコードの警告
    • C#より高度な型推論での記述の省略
    • Option型での無効値の明示、基本的にNULLを用いない(NULLが無いわけでは無いが)
    • computation expressionでの言語内DSLの作成
    • オブジェクト式による無名オブジェクトの簡潔な記述
  • 単位を持った数値のサポート
    • メートルとか秒とかキログラムとかを数値に付けることができて、異なる単位同士の加算がコンパイルエラーになる等の静的検証ができる
  • 末尾再帰な関数をコンパイラが最適化してループ処理に変換してくれる

特に判別共用体は、ドメインモデリングを行う際に型をグループ化するための手段として有用だと思います。

C#ではなくF#を採用する際のリスク、不安な点と、それに対する反論
  • 学習コスト
    • 文法がC#とは大幅に違う
    • 関数型的な記述への慣れが必要
    • 反論 : 関数型的な設計、コーディングスタイルは現状普及しつつあり、学習コストを支払ってでも獲得しておくべきものである
  • 言語としての将来性
    • C#の進化によって、差別化ができなくなっていくのでは?
      • 以前はF#の利点だった非同期処理はasync-awaitで書きやすくなり、REPLもできた。パターンマッチも出来てくる。
    • 反論 :

参考サイト・参考文献

技術習得のレベル

技術習得のレベルについて、最近の思うところ。 プログラミング言語やソフトウェア設計パターン、Webアプリのフレームワークなど、何らかの技術の習得には、以下のようなレベルがあると思う。

  • レベル0:存在すら知らない

  • レベル1:名前だけは知っている

  • レベル2:知っている。 その技術で解決できる問題が何であるかが分かっている。

  • レベル3:使える。 検索したりリファレンスを参照することで、その技術を使って問題を解決できる。

  • レベル4:身についている。 何も参照しなくてもその技術を使える。

  • レベル5:精通している。 その技術を規格文書や内部動作レベルで完璧に理解しており、人に解説したり、いざとなれば改善できる。

自分のコア技術としてレベル4〜5のものは一つ以上欲しい。 また、世の中のデファクトスタンダードな技術群についてはレベル2〜3くらいには引き上げたい。

ユーザビリティにおける探索/実行モデル

ソフトウェアに限らず、何らかの道具のユーザビリティについて、探索/実行モデルというものを考えている。 ユーザビリティ研究において、もっと整理された理論がきっとあるのだろうが、見つけられていない。

前提

  • 人は、何らかの目的を達成するために、手段として道具を使う

探索/実行モデルとは

道具を使う時間を探索フェーズと実行フェーズに分類する。

  • 探索フェーズ : 道具の使い方を探す段階。使い方を検索したり、マニュアルを読んだり、GUI上でボタンを探したりするなど。道具で目的を達成するために、本質的には必要のない部分。
  • 実行フェーズ : 目的を達成するために、道具を操作する段階。ボタンを押したり、コマンドを打ち込んだりするなど。道具で目的を達成するために、必ず必要となる部分。

探索フェーズ、実行フェーズにかかる時間をそれぞれ、探索時間、実行時間と呼ぶことにする。

探索/実行モデルから見た道具への習熟

道具への習熟は、探索時間が短縮されること、と説明できる。

  • 例えば、道具の使い方が分からない間は、マニュアルなどを参照するのに時間がかかるが、慣れてくれば何も見なくても使えるようになる。

  • 習熟によって、最終的には探索時間はゼロに出来る。

  • 一方で、実行時間は習熟してもゼロにはできない。

探索/実行モデルによる「使いやすさ」の評価

道具の「使いやすさ」は、その道具を使って目的を達成するまでの時間によって測ることができる。 そして、「使いやすさ」を習熟段階によって二種類に分類する。

  • 初期段階での使いやすさ : 世間一般で言われる素人向けと呼ばれる道具、または「直観的なUI」は、初期段階での探索時間が短いこと、として説明できる。
  • 習熟後の使いやすさ : 一方で、玄人向けと呼ばれる道具は、実行時間が短いこと、として説明できる。

例えば、コマンドラインユーザーインターフェース(CUI)は、未習熟の状態ではどんなコマンドがあるのか分からないため探索時間が長いが、一度覚えてしまえばコマンドを打ち込むだけで目的を達成できるため実行時間は短い。対して、グラフィカルユーザーインターフェース(GUI)は、画面上のアイコンやメニューにより初期段階の探索時間はCUIと比較して短いが、マウス操作や階層的なメニューの操作により実行時間は長くなる。

UI変更がユーザーに不人気な理由

UIを変更すると、習熟によって短縮された探索時間がリセットされる。そのため、UIの変更により、いくら実行時間が短縮されても、それ以上に探索時間が長くなることが多いため、ほとんどのユーザーはその道具を使って目的を達成するための時間が長くなる。よって、「使い勝手が悪くなった」と言われることになる。

Rでプロットするときにインタラクティブにズームとか移動とかしたい

[2014/09/20 追記]

rChartsパッケージのhPlotでもインタラクティブなズームができるみたい。

参考 : rCharts:Highchartsの使い方

[追記ここまで] 

 

ざっと調べただけ

  • GGOBI + rggobi パッケージ : library(rggobi)でエラー。未調査。
  • iplots パッケージ : JavaJDKのインストールが必要。専用の関数(iplotなど)がある。
  • zoom パッケージ : 通常のplot 後にzm関数でインタラクティブに操作できるウィンドウが出現。マウスでの操作もできるみたいだが、自分のPCでは動かず。
  • playwith パッケージ : playwith関数の引数に通常のplotコマンドなどを渡すと、ウィンドウが立ち上がる。Win 8 64bitではGTK+のインストールがうまくいかなかった。

iplotsとzoomが良さ気。zoom はズーム量が指定できればさらに良いのだが。

参考サイト

Quick-R: Interactive Graphics

関数に渡された実引数を文字列化する

元ネタ -> r - Quick question: cast function argument as a character string? - Stack Overflow

deparse と substitute を使うと実引数を文字列化できる。

> f <- function(x) deparse(substitute(x))
> f(1)
[1] "1"
> f(x)
[1] "x"
> f(list(x=3))
[1] "list(x = 3)"

RでF#のパイプ演算子

2014/03/26追記

magrittrパッケージに以下で書いてるようなパイプ演算子があります。
下でやってることよりもっとちゃんとつくってあるので、
unaryとか必要ないです。

> 1:3 %>% sort(dec=T)
[1] 3 2 1

追記ここまで



Rは演算子の定義ができるので、F#のパイプ演算子っぽいものが作れる。

> "%>%" <- function(x, f) f(x)
>  3:1 %>% sort
[1] 1 2 3

ついでに任意の関数を1引数関数に変換するunaryってのも作っておくと色々と捗るかもしれない。

> unary <- function(f, ...) function(x) f(x, ...) 
> 1:3 %>% unary(sort, dec=T)
[1] 3 2 1

あと、Rは->演算子で左から右への代入ができるので、パイプ演算子と組合せると非常に美しい感じ。

> # ソートしてlogをとって変数xに代入
> 3:1 %>% sort %>% log -> x
> x
[1] 0.0000000 0.6931472 1.0986123

ただし注意点として、複数行に渡る式を書く時は、演算子を行末に置く必要がある。

> # 3:1 %>% sort
> #     %>% log
> # とやろうとすると、1行目で式が評価されちゃう
> 3:1 %>% sort
[1] 1 2 3
> # パイプ演算子は行末に置く必要がある
> 3:1 %>%
+   sort %>%
+   log
[1] 0.0000000 0.6931472 1.0986123

直積集合 (追記:expand.grid)

2012/09/20 追記
R Tips 第2版を購入して読んでいたら、expand.gridなる関数があるじゃないですか・・・。
The R Tips―データ解析環境Rの基本技・グラフィックス活用集

ということで、下でやっていることはexpand.grid関数でできます。
ああ、もっと検索力が欲しい。




Rで直積集合を求める関数が見つからなかったので自作。
任意個の名前付きベクトルを受け取って、直積集合のデータフレームを返す。

direct.product <- function(...) {
  xs <- list(...)
  n <- length(xs)
  ns <- cumprod(sapply(xs, length))
  lapply(1:n, function(i) {
    times <- if(i > 1) ns[i-1] else 1
    each <- ns[n] / ns[i]
    xs[[i]] <<- rep(xs[[i]], times=times, each=each)
  })
  data.frame(xs)
}

使用例

> direct.product(x=1:3, y=4:5, z=6:8)
   x y z
1  1 4 6
2  1 4 7
3  1 4 8
4  1 5 6
5  1 5 7
6  1 5 8
7  2 4 6
8  2 4 7
9  2 4 8
10 2 5 6
11 2 5 7
12 2 5 8
13 3 4 6
14 3 4 7
15 3 4 8
16 3 5 6
17 3 5 7
18 3 5 8