反省はしても後悔はしない

Vim とか備忘録とか。それと関数型言語勉強中

Linux Mint 15 で Vim (GVim) をコンパイル

はじめに

Linux Mint 15 が出たので早速 VM にインストールしてみました。
OS をインストールしたらまずやるのは勿論 Vim のコンパイルですね。apt-get だとちょっと古いので最新の機能を使いたかったらやっぱり自分でビルドするのが良いです。

基本的には vim-jp » Linuxでのビルド方法 と同じですが、Linux Mint 15 だとそのままではだめなので少し変えてます。

とりあえず、リポジトリに追加します

$ sudo vi /etc/apt/souces.list.d/official-package-repositories.list
  deb http://packages.linuxmint.com olivia main upstream import  #id:linuxmint_main

  deb http://archive.ubuntu.com/ubuntu raring main restricted universe multiverse
+ deb-src http://archive.ubuntu.com/ubuntu raring main restricted universe multiverse
  deb http://archive.ubuntu.com/ubuntu raring-updates main restricted universe multiverse

  deb http://security.ubuntu.com/ubuntu/ raring-security main restricted universe multiverse
  deb http://archive.canonical.com/ubuntu/ raring partner

色々入れます

Perl, Python, Python3, Ruby, Lua のインターフェースを使いたいので。あと GVim も。

$ sudo apt-get install mercurial gettext libncurses5-dev libxmu-dev libgnomeui-dev libperl-dev python-dev python3-dev ruby-dev libacl1-dev libgpm-dev libxpm-dev liblua5.1-0-dev lua5.1 tcl-dev

MercurialVim のソースをとってきます

$ hg clone https://vim.googlecode.com/hg/ vim

ビルド

ただし、vim-jp に書いてあるままだと Python のところでこけるのでちょっと修正してます。
参考
Ubuntu 13.04 での Vimコンパイル #Vim #Ubuntu - Qiita [キータ]

$ ./configure --with-features=huge --enable-gui=gnome2 --enable-fail-if-missing --enable-perlinterp --enable-pythoninterp --enable-rubyinterp --enable-python3interp --enable-luainterp --with-python-config-dir=/usr/lib/python2.7/config-i386-linux-gnu --prefix=/path/to/vim
$ make
$ make install

パスを通します

.bashrc や .zshrc などに

export $VIM_HOME=/path/to/vim
export $PATH=$VIM_HOME/bin:$PATH

できた!

Vim のコマンドラインモードを使いこなそう

[Vim]Vim のコマンドラインモードを使いこなそう

この記事は Vim Advent Calendar 2012 の 180 日目の記事です。

昨日は @raa0121 さんの Sapporo.vim(仮称)発足(仮) のお知らせでした。

はじめに

この記事は Vim のカーソル移動などの基本操作を覚えて、自分の vimrc に自分用のマッピングも少しずつ設定できるようになってきたくらいの方向けの記事です。

コマンドラインモードとはなんでしょう?
そう! : (コロン) を押した時のあの下に行くモードです。

だれでもまず最初は :q で Vim の終了や :w で保存をしますよね。

この記事ではそんなコマンドラインモードをもっと便利に使うための Tips なんかを紹介します。

コマンドラインモードでも補完をする

コマンドラインモードでもコマンド名やファイル名などの補完ができます。

ある程度文字を入力してから <C-d> を押します。

その後 <C-l> を押すと、共通部分までは自動で入力してくれます。
このようになんの設定もしなくてもこれくらいの補完はできます。

設定をいじってもっとコマンドラインモードの補完を使いやすくする

前述のとおり、コマンドラインモードでも補完はできるのですが、デフォルトのままだとやはり微妙に使いづらいです。そこで設定をいろいろいじってもっと使いやすい補完にしましょう。

以下の設定を vimrc に追加します。

set wildmenu

これで、文字を入力した後に <Tab>*1 を打つだけで良い感じに補完することができます

さらに、さきほどの wildmenu での補完の挙動は wildmode オプションでいろいろ設定を変えることができます。
私は下のように設定しています。

set wildmode=longest:full,full

これで最初の1回目の <Tab> では共通部分までの補完をしつつ補完候補を出し、次回以降はこの補完候補を順に選択するような動作になります。

ほかにどういう機能や設定があるかは :help cmdline-completion してみてください。

コマンドラインモードでのマッピングを設定する

ノーマルモードでのマッピングは nmap、ビジュアルモードでのマッピングは xmap*2 で設定します。

これらと同様、コマンドラインモードでも cmap というコマンドでマッピングを設定することができます。

といっても何を設定すればいいのかわかりませんね。なので、いろいろ設定例を紹介します。


以下はコマンドラインモードで Emacs 風のキー操作を提供するものです。

cnoremap <C-a> <Home>
" 一文字戻る
cnoremap <C-b> <Left>
" カーソルの下の文字を削除
cnoremap <C-d> <Del>
" 行末へ移動
cnoremap <C-e> <End>
" 一文字進む
cnoremap <C-f> <Right>
" コマンドライン履歴を一つ進む
cnoremap <C-n> <Down>
" コマンドライン履歴を一つ戻る
cnoremap <C-p> <Up>
" 前の単語へ移動
cnoremap <M-b> <S-Left>
" 次の単語へ移動
cnoremap <M-f> <S-Right>

コマンドラインモードでも方向キーなどホームポジションから離れてしまうキーをおそうとすると生産性が低下してしまいます。コマンドラインモードでもなるべくホームポジションにとどまれるようにしましょう。*3


以下は検索時の / のエスケープを簡単に入力できるようにするものです。

cnoremap <expr> / (getcmdtype() == '/') '\/' : '/'

hoge/fuga を検索するときには Vim では hoge\/fuga と入力しないといけませんが、それを簡単に入力できるようになります。ただし、パスの区切りを入力するなど検索以外の時にマッピングされると困るので、getcmdtype を使って / による検索のときだけ \/ というマッピングが働くようにしています。
この設定は vimrc 読書会で誰かから教えて頂きました。*4

追記

? の検索の時は / のエスケープはいらないです。逆に ? のエスケープが必要です。

以下の設定は /? で検索時に現在の検索する語に単語境界を付与するものです。

cnoremap <C-o> <C-\>e(getcmdtype() == '/' <Bar><Bar> getcmdtype() == '?') ? '\<' . getcmdline() . '\>' : getcmdline()<CR>

どういうことかというと、たとえば /hoge と入力した後に <C-o> を押すと /\<hoge\> というように変わります。最初の /hoge では ahoge や hogera など単語の一部に hoge が含まれていても検索に引っかかりますが /\<hoge\> だと hoge という単語しか検索できないようにすることができます。

この設定だと一方的に単語境界を設定するだけですが、VimScript でちょっとした関数を作れば単語境界をトグルさせるようなことも可能です。(私はそのように設定しています)

smartinput プラグインを使ってさらに複雑なマッピングを設定する

上級編です。
smartinput という入力補助系プラグインがあります。これはもともと開き括弧を入力したら自動的に閉じ括弧を入力するというようなプラグインですが、非常に柔軟なカスタマイズができるためいろいろなことに使えます。

今回はこれをコマンドラインモードでのマッピングに応用します。

call smartinput#map_to_trigger('c', 'D', 'D', 'D')
call smartinput#map_to_trigger('c', 'I', 'I', 'I')
call smartinput#map_to_trigger('c', 'C', 'C', 'C')
call smartinput#define_rule({'at': 'Unite \%#', 'char': 'D', 'input': '<C-u>UniteWithInputDirectory<Space>', 'mode': ':'})
call smartinput#define_rule({'at': 'Unite \%#', 'char': 'I', 'input': '<C-u>UniteWithInput<Space>', 'mode': ':'})
call smartinput#define_rule({'at': 'Unite \%#', 'char': 'C', 'input': '<C-u>UniteWithCursorWord<Space>', 'mode': ':'})

これは Unite 系のコマンドを簡単に入力するためのものです。:Unite<Space> まで入力した時点で I を押すと、:UniteWithInput<Space> に変えることができます。Unite の source 名はだいたい小文字で始まるため大文字をマッピングしても問題ないことを利用しています。
:Unite<Space> を入力するマッピングを他に用意しておくと大変便利です。

このように smartinput を使うと、コマンドラインモードで文脈に応じた柔軟なマッピングを設定することができます。

まとめ

ノーマルモードだけではなく、コマンドラインモードも使いこなしてこそ真の Vimmer

最後に

vimrc 読書会に参加すると以下略

追記

最初コマンドモードと書いていましたが、コマンドモードは vi におけるノーマルモードのことで、Vim ではコマンドラインモードというそうです。id:thinca さん教えてくださりありがとうございました。

次回は @ujihisa さんです。

*1:このキーは wildchar オプションで変更できます

*2:vmap はセレクトモードでもマッピングしてしまいます。意味がわからなければビジュアルモードのマッピングは xmap と覚えた方が良いです

*3:気づいた人は気づいたかもしれませんが、実はこれは :help emacs-kes からのコピペです

*4:誰だったかは忘れた

Vim で絶対に OCaml を打ち間違わないための設定

というようなことがあったので。

今までは

abbreviate Ocaml OCaml

と設定することで対処していました。(:help type-mistakes)
しかし、OCamlSpotter のように続いた単語の場合は短縮入力では対応出来ません。

そこで、入力支援系プラグインである smartinput を使って以下のようにします。

call smartinput#map_to_trigger('c', 'l', 'l', 'l')
call smartinput#map_to_trigger('i', 'l', 'l', 'l')

call smartinput#define_rule({'at': 'Ocam\%#', 'char': 'l', 'input': '<BS><BS><BS>Caml', 'mode': 'i:/?'})

smartinput はもともとマッピングを利用して開き括弧 '(' を入力すると自動的に閉じ括弧 ')' を入力するためのものですが、これを応用して l を入力した時にカーソルの前方が Ocam だった場合に cam をバックスペースで消した後自動 Caml を入力するように設定出来ます。

これで怖い人から刺される心配がなくなりますね。

tig なんて目じゃない! Git のログ系 Vim プラグイン gitv & gitv をGit 統合インターフェース化する最強の設定

この記事は Vim Advent Calendar 2012 の 168 日目の記事です。
昨日は id:yonchu さんの accelerated-smooth-scroll という Vimプラグイン を作った (Vim Advent Calendar 2012, 167日目) - よんちゅBlog でした。

はじめに

最近、Git のログを見る系のエントリが多い気がします。今回の Vim Advent Calendar でも

という記事がありましたし、また最近

なんかもありました。
流行ってるのかな???


今回は私が Vim 上で Git のログを見る(+α)ときどうやってるかについて書きます。

gitv!

gitv というプラグインがあります。

上の図のように左側に Git のログ、右側にその時のコミットの diff が表示されます。便利!!
なお、gitv は vim-fugitive に依存しているので vim-fugitive のインストールは必須です。

実は gitv の紹介記事は前書いたことがあるので基本的なことはそちらを参照してください。

vim で gitk 的なことを行う gitv が便利 - 反省はしても後悔はしない。

gitv の使い方応用編

今日の主題はこちらです。
gitv はデフォルトのままでもログを見る分には便利なんですが、それ以外のことはあまり出来ません。*1

そこで、自分の vimrc に gitv を便利に使うための設定を追加します。これにより gitv を単なるログビューアから Git のための統合インターフェースへと昇華させることができます。

下準備

まず準備として vimrc に以下の記述を追加します。

autocmd FileType gitv call s:my_gitv_settings()
function! s:my_gitv_settings()
  " ここに設定を書く
endfunction

*2
gitv を開くと filetype=gitv が設定されるので、これを利用して gitv のバッファだけで使える素敵設定を追加していきます。
以降、設定は上の s:my_gitv_settings() 関数内に書いていきます。

現在のカーソル位置にあるブランチ名を取得してログ上でブランチに checkout する

gitv のログにはブランチ名も表示されますが*3、カーソルをブランチ名に合わせて、そのブランチ名に対して git checkout や reset などの操作ができると便利です。

そのために以下の設定を追加します。

" s:my_gitv_settings 内
setlocal iskeyword+=/,-,.
nnoremap <silent><buffer> C :<C-u>Git checkout <C-r><C-w><CR>

ブランチ名は単なる英数字だけでなく / や - といった記号も使えるので、まずはそれらをキーワードとして含めます。
コマンドモードで <C-r><C-w> を入力するとカーソルの下にあるキーワードを取得できることを利用して、Git のコマンドを組み立てます。
なお、:Git というコマンドは vim-fugitive が提供しているコマンドです。*4
この例だと、ブランチ名の上で C を押すと即座にそのブランチに git checkout することができます。
checkout のところをいろいろ変えればブランチ名を利用する任意のコマンドをすぐに実行することができます。
ちなみに、カーソルを移動させるときは r や R を利用するとブランチの間を簡単に移動できます。

現在のカーソル行の SHA1 ハッシュを取得してログ上であらゆることを実行する

ぶっちゃけ Git はハッシュが全てです。ハッシュの値さえ取得出来れば割となんでも出来ます。いちいち、HEAD からいくつ離れているかを数える必要はありません。

幸い、gitv のウィンドウにはハッシュ値が一番右に表示されています。表示されていれば取得するのは簡単です。

" これは外に定義!
function! s:gitv_get_current_hash()
  return matchstr(getline('.'), '\[\zs.\{7\}\ze\]$')
endfunction

この関数をトップレベルに書いておきます。
あとは、これと expression レジスタ*5を利用して Git のコマンドを組み立てます。

" s:my_gitv_settings 内
nnoremap <buffer> <Space>rb :<C-u>Git rebase <C-r>=GitvGetCurrentHash()<CR><Space>
nnoremap <buffer> <Space>R :<C-u>Git revert <C-r>=GitvGetCurrentHash()<CR><CR>
nnoremap <buffer> <Space>h :<C-u>Git cherry-pick <C-r>=GitvGetCurrentHash()<CR><CR>
nnoremap <buffer> <Space>rh :<C-u>Git reset --hard <C-r>=GitvGetCurrentHash()<CR>

これらの設定により、カーソル位置のコミットに対して rebase, revert, cherry-pick, reset などのコマンドを実行することができます。もちろんそれ以外でもリビジョン*6を指定するコマンドはたいてい可能です。

この設定は非常に強力です。日常の add, commit などは fugitive から(もちろん Vim のマッピングを利用して)実行し、rebase や reset などブランチを使うコマンドを gitv から簡単に行うようにすると、ほぼシェルで git コマンドを叩くことがなくなります。

便利すぎるので、ある日突然の自分の Vim が使えなくなった時に非常にストレスになるので注意しましょう。

ファイルの diff じゃなくて変更されたファイルの一覧が見たい

gitv だけじゃなく tig なんかでもログと一緒にファイルの差分が表示されています。でも時にはファイルの中身よりもどのファイルが変更されたかの方が重要な場合があります。そういう時に、該当のコミットで変更されたファイルの一覧が取得出来れば便利です。

実は、この表示されている diff はファイル名の部分で折りたためるようになっています。すなわち、すべての折りたたみを閉じることでファイル一覧を表示することが可能となります。

この折りたたみを簡単に操作できるように、以下の設定をトップレベルに追加します。

autocmd FileType git setlocal nofoldenable foldlevel=0
function! s:toggle_git_folding()
  if &filetype ==# 'git'
    setlocal foldenable!
  endif
endfunction

そして以下の設定を s:my_gitv_settings 関数内に追加します。

" s:my_gitv_settings 内
nnoremap <silent><buffer> t :<C-u>windo call <SID>toggle_git_folding()<CR>1<C-w>w

これにより、t キーを押すだけですべての diff のウィンドウに対して folding を切り替えます。つまり、ファイルの diff とファイルの一覧を切り替えることができます。
ちなみに、fugitive が nice な foldtext を提供しているのでファイル名だけじゃなくて何行変更があったかも表示されます。*7

まとめ

fugitive と gitv の組み合わせることによって、Git のたいていの作業を Vim 内で完結させることができます。
慣れ過ぎるとシェルで git コマンドを叩くのが苦痛になるので注意。

明日は @s_of_p さんです。

*1:checkout や merge もできますがインターフェースが非常にアレ

*2:例を書いといてなんですが、autocmd には augroup とをつけて vimrc をリローダブルにしましょう

*3:すべてのブランチを表示するには :Gitv --all とします

*4:実は、gitv 上で :Git は上書きされていて fugitive の :Git を呼び出した後、自分自身を更新します

*5:インサート・コマンドモードで<C-r>=を入力することにより Vim の式の結果を挿入できます。:help quote=

*6:ブランチ名とか HEAD^ とか、あとなんだっけ?

*7:foldtext を自分でカスタマイズしてる場合は autocmd FileType git setlocal foldtext=fugitive#foldtext()すると治ります。

はじめてプラグインを作ってみた。それとhelpの書き方など

この記事は Vim Advent Calendar 2012 の 124 日目の記事です。
昨日は @supermomonga さんの vimprocでRubyでプロセス通信・ソケット通信しよう でした。

はじめに

Vim にはもともと colorcolumn という機能があります。
たとえば

setlocal colorcolumn=4

という設定を打ち込むと、下のようになります。

4桁目がハイライトで表示されるようになります。桁位置を綺麗に揃えたいときにたまに役に立ちます。

この colorcolumn を現在のカーソル位置から即座に設定出来れば便利そうだと思い、今回はじめてプラグインを作ってみることにしました。

Vim の colorcolumn の機能を簡単に使うためのプラグインを作ってみた

easy-colorcolumn

インストールは例によって NeoBundle で行います。

NeoBundle 'cohama/easy-colorcolumn'

このプラグインはデフォルトではキーマッピングを提供しないので、.vimrc にこういう設定を書いておきます。(一例です)

nmap <Space>c <Plug>(easy-colorcolumn-toggle)
xmap <Space>c <Plug>(easy-colorcolumn-toggle)
nmap <Space>C <Plug>(easy-colorcolumn-clear)

この設定により、おもむろに <Space>c を押せばカーソル位置の桁が colorcolumn によりハイライトされます。
さらに、ビジュアルモードと合わせて使うと、複数の列を一気に設定できたりもします。わーい(棒

消すときは <Space>C です。現在のバージョンだとプラグイン以外で設定したもの(モードラインや.vimrcに set colorcolumn=.. と書いたもの)も問答無用で消します。余力があれば将来のバージョンで直すかも知れません。

ドキュメント(help)書くときのはなし

プラグインの書き方は @osyo-manga さんの Vim プラグインを github で公開するまで がとても参考になりました。
しかし、この記事ではドキュメントの書き方については言及されていません。
なので、自分なりにまとめます。

まずは doc フォルダを作ってその中に {プラグイン名}.txt というファイルを作ります。

中身を編集していきます。ファイルの1行目に*で囲ったプラグイン名と簡単な説明を書きます。*で囲むとその単語がタグとして認識されるので、:help コマンドで検索できるようになります。その隣の説明は引数なしで:helpしたときに表示されるようになるので書いておくと良いと思います。あと、最後の行にモードラインを書いておきます。ちなみにこの説明は vim のヘルプにあります(:help help-writing)。

 *easy-colorcolumn.txt*  easy to use a |colorcolumn| feature

 vim:tw=78:ts=8:ft=help:norl:noet:fen:fdl=0:

書いたら :e して filetype を help にします。色がつきます。
tw (textwidth) を78に設定するのがデフォルトっぽいです。書くときは1行の文字数が78を超えないようにしましょう。基本的には Vim が自動でフォーマットしてくれますが、追記したりすると超えることがあります。その時は gq コマンドを使いましょう。

それと、@thinca さんの Vim プラグインのヘルプを書く人がするべき設定 にある設定をしとくと良いと思います。
あとは help の中身を書いていきます。まずは最初にAuthor (作者) と License を書いて自己主張しておきましょう。
その後は

  • Contents (目次)
  • Introduction (概要)
  • Install (インストール方法)
  • Mapping
  • Usage (使い方)
  • などなど

を書いていきます。
これらは適宜セクション区切り(====)で区切ります。また、セクションのはじめにヘルプタグを付けておくようにします。既存のタグと被らないように {プラグイン名}-introduction などとします。

==============================================================================
INTRODUCTION                                   *easy-colorcolumn-introduction*

なんとかかんとかの概要です。

最後にContents(目次)のところにセクションのはじめに書いたタグを集めます。

Contents *easy-colorcolumn-contents*

Introduction    |easy-colorcolumn-introduction|
Mappings        |easy-colorcolumn-mappings|
....


完成!

正直プラグインのヘルプ書くときは他の人の書いたものを真似すればすぐ書けます。

まとめ

  • プラグインを作るのは思ったより簡単
  • ドキュメント書くまでがプラグイン
  • でもドキュメント書くのも簡単

今回のことでプラグイン書くのも面白いなと思ったので、今後も思いついたらいろいろ作って行きたいと思います。
cohama の今後の活躍にご期待ください。


次回は @deris0126 さんです。

Functional 花見 2013 に行ってきた

Functional 花見 2013 に行ってきました。
天気は良いし、桜は満開で最高のお花見日和でした。
そして、なぜか電源もWi-Fiもあるったのでハッカソンにも最適な環境でした。

本日の成果

  • @ さんに opam という、OCaml 用のパッケージ管理ツールを教えてもらいました。早速、インストールしようとしたらハマッてしまったのですがそれも mzp さんが一瞬で解決してくれました。mzp さんすごい。
  • @ さんから F# から JavaScript にコンパイルする FunScript というものの存在を教えてもらいました。コンパイルだけでなく、TypeScript の型定義ファイルから TypeProvider で F# の型をつくることもできるらしいです。時間があれば遊んでみたいです。
  • TaPL 和訳本をちょっと解説してもらいました。あと、@ さんと @ さん(多分)に一番最初の演習問題を解説してもらいました。TaPL の訳本は本屋で少し立ち読みして第2章の最初のページですでに圧倒されてしまって買うのを諦めてたのですが、今日の話では2章はとりあえず流して3章から読めばよいとのことだったので今度時間があるときに買って勉強してみようと思います。
  • @ さんと @ さんにそそのかされ勧められて Coq をインストールしようとしました。前半で opam を入れていたので opam install coq で簡単に入るはずだったのですが、あまりのビルドの遅さにバッテリーが耐え切れずにインストール中に強制シャットダウンとなってしまいましたが・・・。→お家帰ってからちゃんと入れました! Coq も今までは名前だけ知っているだけでよく分かっていなかったのですが、今日話を聞いてやっと Coq の概要を理解しました。少し興味が湧いたので触ってみたいです。
  • @ さんが幻の焼酎である「森伊蔵」を持ってきてくれたので少しだけ頂きました。地元鹿児島でもなかなか手に入らないだけあってすごくおいしくて飲みやすい焼酎でした。sunotora さんありがとうございました。
  • 来栖川電算さんのオフィス見学をさせて頂きました。他社さんのオフィスを見学する機会なんてほとんどないので、新鮮でした。広々とした机にデスクトップマシンがどーんとあり、まさにエンジニアのためのオフィスといった感じで、羨ましかったです。来栖川電算さんありがとうございました。


上に書いた以外にもいろいろ興味深い話をいろいろ聞かせて頂きました。花見のはずなのに勉強会以上に勉強になった気がします。みなさんありがとうございました。

課題としては、インプットばかりで自分からアウトプットが全然ないことですかね〜。もっと勉強してアウトプットもできるようになりたいです。

最後に、主催のITプランニングさん、電源確保してくださった @ さん、あと前日の夜から場所取りをしてくださった方、買い出しに行ってくださった方ありがとうございました。

今更ながら F# で逆 FizzBuzz を解いてみた。それと List モナド

背景

先週のなごやかScala #9にて、逆 FizzBuzz 問題というものを Scala で解いたものをコードレビューするというのがありました。
はてなダイアリーふっかつと逆FizzBuzz問題をScalaで解こう(前編) - スノトラさんのつれづれ日記
元ネタは多分これ
逆FizzBuzz問題 (Inverse FizzBuzz) - 猫とC#について書くmatarilloの雑記


私はその時は問題を知らなかったのでコードレビューの時は聞き流していたのですが、帰ってから調べてみたら面白そうだったので挑戦してみることにしました。Scala で書くのは sunotra さんにおまかせするとして、私は F# でやってみました。

ひけっていけいさん → List モナド

元ネタの Scala のコードを読むと [1..1], [1..2], ... , [1..100], [2..2], [2..3], ... というリストのリストから答えとなる文字列にが合致するものを探すアルゴリズムになっています。(正確にはすべての要素を FizzBuzz 化させたものを Map にしていて、入力の文字列のリストをキーとして答えを取り出している)
答えの候補となるものをとりあえず全部並べていく = 非決定計算ということで、コンピュテーション式を使った List モナドで実装してみました。
実はコンピュテーション式よくわかっていないので*1、List モナドのところは以下を参考にしました。
F#で順列(Permutation)と組み合わせ(Combination)。YOU、Listモナドしちゃいなよ。集合モナドもあるよ。 - Bug Catharsis

書いてみた

let fizzbuzz x =
    match (x%3, x%5) with
    | 0, 0 -> ["FizzBuzz"]
    | 0, _ -> ["Fizz"]
    | _, 0 -> ["Buzz"]
    | _, _ -> []

let tofizzbuzz xs = List.collect fizzbuzz xs

type ListBuilder() =
    member this.Bind (m, f) = m |> List.map f |> List.concat
    member this.Return x = [x]
    member this.Zero () = []

let list = ListBuilder()

let answers input = list {
    let! start = [1..100]
    let! end = [start..100]
    if tofizzbuzz [start..end] = input then
        return [start..end]
}

let inverse_fizzbuzz input =
    match answers input with
    | [] -> []
    | xs -> List.minBy List.length xs

printfn "%A\n" <| inverse_fizzbuzz ["Fizz"; "Buzz"; "Fizz"; "FizzBuzz"; "Fizz"; "Buzz"]
(* [9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20] *)

上のコードの answers のところでコンピュテーション式を使っています。本来なら2重ループになるところがすっきり書けていて素敵ですね。

逃げ

F# 力低いので上のコードは突っ込みどころ満載かも知れません。

*1:なんで Zero がいるの?とか