Tambourine作業メモ

主にスキル習得のためにやった作業のメモ。他人には基本的に無用のものです。

データモデリングをしようと思い、DataGripを使ってみることにする

でっかいSIerでお仕事をしていると、データモデリングをすることが・・・それほどはない。だいたい、そのポジションは1アプリケーションにつき、1人が専任してやることが多いからだ。それぐらいデータモデリングは統一された考え方が必要だし、重要なものだ。私はちゃらんとかぽらんとかしている人間で、データモデルの管理を任せられたことなんかないのだ。

とはいえ、ちょっとした作業の仕組みをデータモデリングを使って表現したいことだってある。人間の活動というのは、だいたいがデータ処理なのだ。そこではわかりやすい絵で表現することが大事だ。ベースがデータモデリングなのだからデータベースの管理ツールを使って書ければそれがよい。後で他の時にも役にたつかもしれないし。PowerPointで書くのは出来れば避けたい。なぜなら、泣きそうになるからだ。

というわけで、なんか新しいツールを触ってみるべと思い、最近とみに評判のよいJetBrainsのページを見てみる。DB管理ツールがあるので、これを使ってみることにする。DataGridという名前だ。ほっくりと有料だが、例によって1ヶ月間の試用期間がある。月額¥2290とあるので、使いたくなったときにポチッとお金を払うのでも良さそうだ。払ったことないので、ライセンス管理をどうやってやるのかしらないけど。

さて、dmgがダウンロードできたので(今回はdatagrip-2019.1.4.dmgというファイル名だった)、インストールしてみよう。そういえば、こないだMacがあぱらぱーになって工場出荷状態に戻して以来、初めてインストールするJetBrains IDEものだ。

さっそく起動すると以下のウィンドウからスタートだ。

f:id:Tambourine:20190713135831p:plain
設定の取り込み

"Do not import settings"で進む。リーガル・アグリーメントがでるので、アグリーしちゃう。アグリー。

f:id:Tambourine:20190713140139p:plain
User Agreement

よく聞かれる情報を送っていいかダイアグラム。お好きにどうぞ

f:id:Tambourine:20190713140322p:plain
診断情報の送信確認

お金を払える。とりあえず、払わない。

f:id:Tambourine:20190713140834p:plain
License Activation

UIテーマを選ぶ。私、いわゆるダークモードって嫌いです。

f:id:Tambourine:20190713141005p:plain
UIテーマ

接続するDBの種類を選ぶ。お絵かきするだけなのでなんだっていいんだけど、あとでAWSのDBでも遊んでみたいのでMariaDBを選んどく。 Directoryはよくわからないので、とりあえず何も書かないでいってみよう。

f:id:Tambourine:20190713150730p:plain
Database Option

というわけで、やっとこさ起動した。

f:id:Tambourine:20190713150929p:plain
起動直後

せっかくだからDBに接続してみよう。AWSコンソールからRDSを選ぶ。ちなみに、私のAWSアカウントは作ってIAMのセキュリティ設定だけしかしてないすっからかんのアカウントである。

f:id:Tambourine:20190713151432p:plain
RDS

しょっぱいDBを作っとく。DB名は恥ずかしいので秘密(謎)。あえて簡単設定を選んでみよう。

f:id:Tambourine:20190713152543p:plain
DBの作成1

f:id:Tambourine:20190713152606p:plain
DBの作成2

作成中。

f:id:Tambourine:20190713153514p:plain
作成中

[認証情報の詳細を表示]をクリックすると

f:id:Tambourine:20190713153532p:plain

ここにパスワードが出るので、メモっておく。作成が完了したら、DB識別子のリンクをクリックして、

f:id:Tambourine:20190713160050p:plain
接続情報

エンドポイントとポートをメモる。これを使って接続する。

DataGripでCommand-Nすると、メニューが出てくる。

f:id:Tambourine:20190713170845p:plain
Newメニュー

[Data Source from URL]を選ぶ。

f:id:Tambourine:20190713170912p:plain
シンプルなダイアログ

エンドポイントをURLに入れる。ある程度埋まった設定画面が出る。

f:id:Tambourine:20190713171227p:plain
設定画面

[Test Connection]を押すと、テスト接続できる。

で、すんなりとは接続できなかった。

  1. ドライバが入ってない。これは[Test Connection]を押すとダウンロードしてくれるので問題ない。
  2. Connection Time Outになる。簡単設定でつくると、パブリックアクセシビリティがオフになってるので、オンに変更する。
  3. それでもタイムアウト。考えてみれば、DBインスタンスと同時にできたVPCの接続設定がされてない。VPCセキュリティグループにMyIPからの3306を許可

f:id:Tambourine:20190713172130p:plain
VPCセキュリティグループ

これで繋がるようになった。

長くなったので、つづく。

プロセスフローを書きたいと思い、BlueworksLiveに出会った

でっかいSIerでお仕事をしていると、プログラムコードを書くよりもビジネスプロセスを書く方がよっぽど頻度が高い(個人の感想です)。

ビジネスプロセスというとスイムレーンが水平に並んでいて、お仕事が左から右にフローチャートみたいな書式で流れていく、 あんな感じの図を思い浮かべる人も多いだろう。アレだ。

DFDを思い浮かべてしまう人や、シーケンス図を思い浮かべてしまう人もいるだろうか。いないか。

アーキテクトたるもの、図を書くものである。むしろ、抽象概念を図で表すことが主なアーキテクトの仕事と言っても良い。これをモデリングという。なので、アーキテクトは様々なものを図にする。図にするのは、複雑なものを単純にして理解しやすくするという意味もあるが、誰か他の人にモデリング対象がどうなっているか伝えるという大きな意味もある。というわけで、図の書き方、記法というものはアーキテクト間で共有されてなければならない。

そんなわけで、ビジネスプロセスも図にするときには記法がある。BPMN(Business Process Model and Notation)という。このBPMNに従った図をラクチンに書く方法が欲しい。Excelやパワーポイントで書くと泣きそうになるからだ。

己のクリック力を最大限まで高めて検索してみたが、お手頃であんまりいいツールはない。マネーを支払って良いのならIBMクラウドツールであるBlueworksLiveが良い事がわかった。灯台下暗しである。1ユーザー月額$53。日本円で買うとなぜか¥7,345。まあ、良い値段だけどお仕事ツールとしてめちゃ高いわけでもない。1ヶ月試用できるので、その間に仕事を終わらすのがナイス。

何が灯台の下だったかというと、私は何を隠そうIBM社員であり、社員はタダで使えると言うことにログインしてみて初めて気がついたからである。なので、ここでBlueworksLiveをべた褒めしているのは社員だということに注意して欲しい。社員も知らないようなツールなのかよということも、あまり突っ込まないで欲しい。デカい会社というのはそんなものだ。

というわけで、そんなにたくさんの種類のお絵かきツールを使ったわけではないし、BPMNを活かすような使い方もしていないけど、ビジネスフロー作成ツールとしてはBlueworksLiveはかなり良い感じだった。ログインするとまずチュートリアルのビデオを見ててと言われるので(英語を我慢して)見て欲しい。はー、よくできてんなーと思ってもらえると思う。

w3mをインストールしたら、opensslがインストールされて・・・あとでみる

ちょっと必要があってw3mをhomebrewでインストールした。 途中でopensslがインストールされてCaveatsがいっぱいでてるけど、 後で読む。そのためにとりあえずここに貼っときます。

> brew install w3m
==> Installing dependencies for w3m: bdw-gc and openssl
==> Installing w3m dependency: bdw-gc
==> Downloading https://homebrew.bintray.com/bottles/bdw-gc-8.0.4.mojave.bottle.
==> Downloading from https://akamai.bintray.com/05/05219d7d030791e3c3e3751b36a60
######################################################################## 100.0%
==> Pouring bdw-gc-8.0.4.mojave.bottle.tar.gz
🍺  /usr/local/Cellar/bdw-gc/8.0.4: 69 files, 1.5MB
==> Installing w3m dependency: openssl
==> Downloading https://homebrew.bintray.com/bottles/openssl-1.0.2s.mojave.bottl
==> Downloading from https://akamai.bintray.com/c4/c4a762d719c2be74ac686f1aafabb
######################################################################## 100.0%
==> Pouring openssl-1.0.2s.mojave.bottle.tar.gz
==> Caveats
A CA file has been bootstrapped using certificates from the SystemRoots
keychain. To add additional certificates (e.g. the certificates added in
the System keychain), place .pem files in
  /usr/local/etc/openssl/certs

and run
  /usr/local/opt/openssl/bin/c_rehash

openssl is keg-only, which means it was not symlinked into /usr/local,
because Apple has deprecated use of OpenSSL in favor of its own TLS and crypto libraries.

If you need to have openssl first in your PATH run:
  echo 'set -g fish_user_paths "/usr/local/opt/openssl/bin" $fish_user_paths' >> ~/.config/fish/config.fish

For compilers to find openssl you may need to set:
  set -gx LDFLAGS "-L/usr/local/opt/openssl/lib"
  set -gx CPPFLAGS "-I/usr/local/opt/openssl/include"

For pkg-config to find openssl you may need to set:
  set -gx PKG_CONFIG_PATH "/usr/local/opt/openssl/lib/pkgconfig"

==> Summary
🍺  /usr/local/Cellar/openssl/1.0.2s: 1,795 files, 12.0MB
==> Installing w3m
==> Downloading https://homebrew.bintray.com/bottles/w3m-0.5.3_6.mojave.bottle.t
==> Downloading from https://akamai.bintray.com/57/571d0562f50fb42eab8fc7efd03e7
######################################################################## 100.0%
==> Pouring w3m-0.5.3_6.mojave.bottle.tar.gz
🍺  /usr/local/Cellar/w3m/0.5.3_6: 28 files, 1.8MB
==> `brew cleanup` has not been run in 30 days, running now...
Removing: /Users/tambara/Library/Caches/Homebrew/heroku--7.24.3.tar.xz... (6.5MB)
Removing: /usr/local/Cellar/openssl/1.0.2r... (1,795 files, 12.1MB)
Removing: /Users/tambara/Library/Caches/Homebrew/openssl--1.0.2r.mojave.bottle.tar.gz... (3.7MB)
Removing: /Users/tambara/Library/Caches/Homebrew/pandoc--2.7.2.mojave.bottle.tar.gz... (13.4MB)
Removing: /Users/tambara/Library/Caches/Homebrew/pyenv--1.2.11.mojave.bottle.tar.gz... (623.7KB)
Removing: /Users/tambara/Library/Caches/Homebrew/ruby-build--20190423.tar.gz... (60.7KB)
Removing: /Users/tambara/Library/Logs/Homebrew/pyenv... (64B)
Removing: /Users/tambara/Library/Logs/Homebrew/pkg-config... (64B)
Removing: /Users/tambara/Library/Logs/Homebrew/tree... (64B)
Removing: /Users/tambara/Library/Logs/Homebrew/pandoc... (64B)
Removing: /Users/tambara/Library/Logs/Homebrew/heroku... (114B)
Removing: /Users/tambara/Library/Logs/Homebrew/rbenv-communal-gems... (115B)
Removing: /Users/tambara/Library/Logs/Homebrew/readline... (64B)
Removing: /Users/tambara/Library/Logs/Homebrew/ruby-build... (2 files, 179B)
Removing: /Users/tambara/Library/Logs/Homebrew/nodebrew... (104B)
Removing: /Users/tambara/Library/Logs/Homebrew/heroku-node... (119B)
Removing: /Users/tambara/Library/Logs/Homebrew/autoconf... (64B)
Removing: /Users/tambara/Library/Logs/Homebrew/pcre2... (64B)
Removing: /Users/tambara/Library/Logs/Homebrew/openssl... (64B)
Removing: /Users/tambara/Library/Logs/Homebrew/fish... (64B)
Removing: /Users/tambara/Library/Logs/Homebrew/rbenv... (64B)
Pruned 0 symbolic links and 2 directories from /usr/local
==> Caveats
==> openssl
A CA file has been bootstrapped using certificates from the SystemRoots
keychain. To add additional certificates (e.g. the certificates added in
the System keychain), place .pem files in
  /usr/local/etc/openssl/certs

and run
  /usr/local/opt/openssl/bin/c_rehash

openssl is keg-only, which means it was not symlinked into /usr/local,
because Apple has deprecated use of OpenSSL in favor of its own TLS and crypto libraries.

If you need to have openssl first in your PATH run:
  echo 'set -g fish_user_paths "/usr/local/opt/openssl/bin" $fish_user_paths' >> ~/.config/fish/config.fish

For compilers to find openssl you may need to set:
  set -gx LDFLAGS "-L/usr/local/opt/openssl/lib"
  set -gx CPPFLAGS "-I/usr/local/opt/openssl/include"

For pkg-config to find openssl you may need to set:
  set -gx PKG_CONFIG_PATH "/usr/local/opt/openssl/lib/pkgconfig"

C++でも遊んでみたい

2019年にもなって、C++を勉強しなくてはいけなくなった。嘆かわしい。

C++は大変に歴史があり、かつ、あり得ないぐらいに幅広く使われているので、大量の歴史的経緯を抱えている。 そのため、実体がよくわからない。私が最後にC++を勉強したのは20世紀のことなので、2019年モデルへの アップデートが必要である。まあ、ぶっちゃけ何にもおぼえてないので、位置から勉強する。

まずは教科書選びが必要。どれが良いのかはさっぱりわからないが、ここはC++の作者であるStroustrupさん自らが書いた、わりと新しめの教科書があるので、それを使ってみよう。もう一冊、いわゆるバイブル(言語作者自らが書いた本)があるようだけど、1万円ぐらいするのでこっちにしとく。

C++によるプログラミングの原則と実践

C++によるプログラミングの原則と実践

この本は、C++の本というよりもC++でプログラミングを学ぶ本だ。これはこれでStroustrupさんの思想信条が伝わってくる気がするのでいいと思っているけども、流石に大学1年生ではないので、すごい勢いで飛ばし読みすることになる。なんせ1200ページもあるのだ。基本、ざっと読んで、練習問題をやっていく感じになる。

まずは、第2章のドリルから。お約束のHello, Worldである。最大の難関だ。

以下のコードを、ビルドして実行せねばならない。

#include "std_lib_facilities.h"

int main()
{
    cout << "Hello, World\n";
    keep_window_open();
    return 0;
}

付録に環境構築の方法があるからそこを見て頑張れと書いてあるが、 そこに書いてあるのはVisual Studio のExpress版のインストール方法である。おまいがー。

とはいえ、Windowsと違って私のMacGCCが入っていなければロクに動くはずもない。 コマンドを打てばどうにかなるはずだ。打たずにどうにかしたいのだ。ぶっちゃけcargo runしたいのだ。ワガママだ。

とりあえず、Visual Studio Codeを立ち上げて、さっきのコードをhello.cppとして保存したところ 「ちょ、おまっ。お前、C++書いたな?書いたんだな?じゃあ、このプラグイン入れるか?入れるよな?」(超訳)と VS Codeが仰いますので、おとなしくプラグインを入れる。 入れて立ち上げ直して、メニューの[Run Build Task...]を選ぶと、clangを使うかg++をつかうかリストで選ばされる。 特に理由もなくg++を選ぶ。もちろんエラーである。

> Executing task: /usr/bin/g++ -g /Users/tambara/study/cpp_study/hello.cpp -o /Users/tambara/study/cpp_study/hello <

/Users/tambara/study/cpp_study/hello.cpp:1:10: fatal error: 'std_lib_facilities.h' file not found
#include "std_lib_facilities.h"
         ^~~~~~~~~~~~~~~~~~~~~~
1 error generated.
The terminal process terminated with exit code: 1

Terminal will be reused by tasks, press any key to close it.

とりあえず、コマンドを打たなくてもg++コマンドを実行してくれたことが驚きだ。世の中は進歩している。 しかしながら、エラーだ。「std_lib_facilities.hってなんすか?」と聞かれている。

うん、私も同感だ。

どうやらこれは学習用にStroustrup先生が用意してくれたヘッダファイルのようだ。 ダウンロードして使えばいいらしい。これを使わないのならば、代わりに

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
inline void keep_window_open() { char ch; cin >> ch; }

と書けとしてある。なるほど、教育的である。ちなみに最後のkeep_window_open()は何かというと、 WindowsでVSからビルドして実行したときにコマンドプロンプトの画面が一瞬で閉じてしまうと なにが起きたかわからないので用意された関数である。教育的すぎて凄い。

先生のサイトからダウンロードし、cppファイルと同じディレクトリにおいてみる。

再度、ビルドしてみよう。

> Executing task: /usr/bin/g++ -g /Users/tambara/study/cpp_study/hello.cpp -o /Users/tambara/study/cpp_study/hello <

In file included from /Users/tambara/study/cpp_study/hello.cpp:1:
In file included from /Users/tambara/study/cpp_study/std_lib_facilities.h:34:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/ext/hash_map:213:5: warning: Use of the header
      <ext/hash_map> is deprecated. Migrate to <unordered_map> [-W#warnings]
#   warning Use of the header <ext/hash_map> is deprecated.  Migrate to <unordered_map>
    ^
In file included from /Users/tambara/study/cpp_study/hello.cpp:1:
/Users/tambara/study/cpp_study/std_lib_facilities.h:43:20: error: no matching function for call to object of type 'hash<char *>'
            return hash<char*>()(s.c_str());
                   ^~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/ext/__hash:38:12: note: candidate function not
      viable: 1st argument ('const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::value_type *'
      (aka 'const char *')) would lose const qualifier
    size_t operator()(char *__c) const _NOEXCEPT
           ^
1 warning and 1 error generated.
The terminal process terminated with exit code: 1

Terminal will be reused by tasks, press any key to close it.

warningはdeprecateだよと言われているだけなのでいいかな。

エラーの方はさっぱりわからないが、ググったらまったく同じようにこの本を使って勉強している人がStack Overflowに書き込みをしている。

stackoverflow.com

なるほど、わからん。

でも、constを付ければ良いようだ。その通りに修正してみる。

> Executing task: /usr/bin/g++ -g /Users/tambara/study/cpp_study/hello.cpp -o /Users/tambara/study/cpp_study/hello <

In file included from /Users/tambara/study/cpp_study/hello.cpp:1:
In file included from /Users/tambara/study/cpp_study/std_lib_facilities.h:34:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/ext/hash_map:213:5: warning: Use of the header
      <ext/hash_map> is deprecated. Migrate to <unordered_map> [-W#warnings]
#   warning Use of the header <ext/hash_map> is deprecated.  Migrate to <unordered_map>
    ^
1 warning generated.

Terminal will be reused by tasks, press any key to close it.

おー、ちゃんと実行ファイルができた。

> ./hello
Hello, World
Please enter a character to exit

a

attr_accessorを自分で実装する

自作クラスでデータを保持しようとしていて半角カナが入ってきて発狂し、絶叫し、半角カナ絶対殺すマンが覚醒した。

といっても、Rubyで半角カナを全角カナに変換するのはとても簡単。NKFを使う。

> irb
irb(main):001:0> require 'nkf'
=> true
irb(main):002:0> a = "いささかプギャーな気分"
=> "いささかプギャーな気分"
irb(main):003:0> NKF.nkf("-Xw", a)
=> "いささかプギャーな気分"

これをセッターに組み込めばいい

require 'nkf'

class MyRecord
  @rec = ""
  
  attr_reader :rec
  
  def rec=(str)
    @rec = NKF.nkf("-Xw", str)
  end
end

r = MyRecord.new

r.rec = "いささかプギャーな気分"

p r.rec # => "いささかプギャーな気分"

問題ない。しかし、フィールドは1つではないかもしれない。 3つあったらこうなるだろう。

class MyRecord
  @rec1 = ""
  @rec2 = ""
  @rec3 = ""
  
  attr_reader :rec1, :rec2, :rec3
  
  def rec1=(str)
    @rec1 = NKF.nkf("-Xw", str)
  end
  
  def rec2=(str)
    @rec2 = NKF.nkf("-Xw", str)
  end
  
  def rec3=(str)
    @rec3 = NKF.nkf("-Xw", str)
  end
end

半角カナ絶対殺すマンは転生し、絶対DRY星人として蘇った。

さて、どうするか。こんな感じに出来ればいいのだろう。

class MyRecord
  @rec1 = ""
  @rec2 = ""
  @rec3 = ""
  
  my_attr :rec1, :rec2, :rec3
end

my_attrはMyRedordクラスのクラスメソッドとして定義してやればいい。 Rubyist人生で初めてinstance_variable_setを使った。

require 'nkf'

class MyRecord
  def self.my_attr(*name)
    attr_reader *name
    name.each do |n|
      define_method("#{n.to_s}=") do |str|
        instance_variable_set(
          "@#{n.to_s}".to_sym,
          NKF.nkf("-Xw", str)
        )
      end
    end
  end
  
  @rec1 = ""
  @rec2 = ""
  @rec3 = ""
    
  my_attr :rec1, :rec2, :rec3
end

r = MyRecord.new

r.rec3 = "いささかプギャーな気分"

p r.rec3 # => "いささかプギャーな気分"

treeコマンド

treeコマンドは、たまーに欲しくなる。 プロジェクトの共有ディレクトリの使い方を説明するととかに。

Macにはない。Homebrewで入れれば良いらしい

> brew install tree
==> Downloading https://homebrew.bintray.com/bottles/tree-1.8.0.mojave.bottle.ta
######################################################################## 100.0%
==> Pouring tree-1.8.0.mojave.bottle.tar.gz
🍺  /usr/local/Cellar/tree/1.8.0: 8 files, 117KB

試して見る

> ls
納品      課題      成果物
> tree
.
├── ?\215?\223\201
├── 課?\214
└── ?\210\220?\236\234?\211?

3 directories, 0 files

おおぅ。-Nが必要らしい

> tree -N
.
├── 納品
├── 課題
└── 成果物

3 directories, 0 files

絶対忘れるので、aliasを設定しておく

> alias tree "tree -N"
> tree 
.
├── 納品
├── 課題
└── 成果物

3 directories, 0 files

fishのaliasは、実体としては関数定義。なので、関数を保存する必要がある。

> funcsave tree

確認してみよう

> functions
., :, N_, abbr, alias, bg, cd, cdh, contains_seq, delete-or-exit, dirh, dirs,
disown, down-or-search, edit_command_buffer, eval, export, fg,
fish_breakpoint_prompt, fish_clipboard_copy, fish_clipboard_paste, fish_config,
fish_default_key_bindings, fish_default_mode_prompt, fish_fallback_prompt,
fish_hybrid_key_bindings, fish_indent, fish_key_reader, fish_md5,
fish_mode_prompt, fish_opt, fish_print_hg_root, fish_prompt,
fish_sigtrap_handler, fish_title, fish_update_completions, fish_vi_cursor,
fish_vi_key_bindings, fish_vi_mode, funced, funcsave, grep, help, history,
hostname, isatty, kill, la, ll, ls, man, nextd, nextd-or-forward-word, open,
popd, prevd, prevd-or-backward-word, prompt_hostname, prompt_pwd, psub, pushd,
pyenv, rbenv, realpath, seq, setenv, string, suspend, trap, tree, type, umask,
up-or-search, vared, wait,
> functions tree
# Defined in - @ line 1
function tree --description 'alias tree tree -N'
    command tree -N $argv;
end
> 

OCamlでコマンドライン引数を処理する

さて、次はファイル名や表示行数を指定できるように修正しよう。

引数は、Cのargv相当がSys.argvに入ってくるが、自分でパースするのはめんどうだ。 もちろん、標準ライブラリでパースできる。Argモジュールを使う。

以下の手順を踏む。

  1. 引数で指定された情報を格納する参照を定義する
  2. どんな引数がくるのか、定義を作る
  3. パースする

詳しいことは、 http://caml.inria.fr/pub/docs/manual-ocaml/libref/Arg.html を読む。 手順2のanon_funに何を指定するのかわかりにくいが、要するにフラグ無しの引数の扱いを指定すればいい。

tail -n 5 filename に対応したかったら、

let n = ref 5 (* 表示する行数 *)
let inputfile = ref ""

let spec = [("-n", Arg.Set_int n, "Number of lines")]
let () = Arg.parse spec (fun s -> inputfile := s) ""


(* ファイルを全行読み込む *)
let fi = open_in !inputfile

let lines = 
  let lst = ref [] in
  let eof = ref false in 
  while not !eof do
    try lst := !lst @ [(input_line fi)] with End_of_file -> eof := true
  done;
  !lst

let () = close_in fi


(* 行のリストの最後n行だけ取り出す*)
let rec tail lst = match lst with
    [] -> [] 
  | first :: rest -> 
    let last = tail rest in
    if List.length last > !n - 1  then last else first :: last 

let () = List.iter print_endline (tail lines)

これでOK。以外とバリバリrefを使うのである(笑)。