Vimで選択部分のテキストを取得する
Vimで選択した部分を取得する
How to get visual selected text in Vim
vimでscriptを書いていると,選択した部分にのみ結果を反映したいみたいなことがあります.
この方法を自分で色々調べていたのですが How to get visually selected text in VimScrip
こんな資料が見つかったりするわけです.
方法その1 選択部分のエリアを探して,その部分を取得する
function! GetVisualSelection() " Why is this not a built-in Vim script function?! let [lnum1, col1] = getpos("'<")[1:2] let [lnum2, col2] = getpos("'>")[1:2] let lines = getline(lnum1, lnum2) if lnum1 == 0 && lnum2 == 0 && col1 == 0 && col2 == 0 return '' endif let lines[-1] = lines[-1][:col2 - (&selection == 'inclusive' ? 1 : 2)] let lines[0] = lines[0][col1 - 1:] return join(lines, "\n") endfunction
だけど,この方法なんだかVimっぽくないと思ったので
自分で色々試行錯誤してみれば,案の定ありました! もっと簡単な方法.
それが以下
方法その2(推奨) クリップボードレジスタを使う
clipboarレジスタの設定で
set clipboard=unamed,autoselectplus
が有効になっていれば
以下の方法で選択部分のテキストが取得できます.
let l:text = @*
簡単!
Have a Good Vim Life
Pythonで末尾再帰最適化
末尾再帰最適化
Pythonでは本来, 末尾再帰最適化は行われないのですが デコレータを使うことによって一発で末尾再帰化を行ってくれるようになります.
今日は,そんな末尾再帰最適化について紹介します.
末尾再帰
そもそも末尾再帰ってなんだろう ってことですが, 末尾再帰とはそのままでは 関数の末尾(関数の値を返す部分)が再帰呼出しとなるような関数 のことです 末尾再帰だと機械的に最適化しやすいので,このように呼ばれるわけです.
再帰はプログラムが簡潔にかけ,見た目には非常に良いのですが 関数の再帰的に呼び出すたびに,関数の呼び出し元情報を記録するスタックが積まれる, 処理的に効率が良くなかったりします.
例えば, 以下のようなn番目のフィボナッチ数列の値を求める関数は 再帰を利用した関数です.(末尾再帰ではありません;足し算を行っています)
def (n): return fib(n-2) + fib(n-1) fib(100)
を書いた時, 以下のような例外が投げられます
RuntimeError: maximum recursion depth exceeded
再帰が深くなりすぎて,スタックが溢れて
このような例外が発生するわけです.
さてこれを末尾再帰な形に書き換えてみましょう
def fib(n, val=1): return fib(n-1, val + n) if n > 0 else val fib(100)
結果を保存するようの変数valを設け, 再帰呼出しでそれを渡しています. ただし,これでも上記と同じ例外が投げられます.
関数の流れを自分で一回追ってみて欲しいのですが, この関数の形では, fib(n, val) → fib(n-1, val →… fib(1, val) という形で呼び出されるのですが
再帰を呼び出した時点で, 必要な計算(valに結果が含まれている)は全て終えているため その関数は再帰呼び出しした関数の値を返すだけ, というほぼ何もしない処理なんですね
展開してやると
return (return (return (return val)))
みたいな形になるわけです. 無駄ですね.
この無駄を無くすように(コンパイラ等で)最適化してやるのが末尾最適化です.
Decoratorによる末尾最適化
New Tail Recursion Decorator (Python recipe) Tail call optimization in Python Pythonのクロージャで末尾再帰最適化をする。
基本はこちらのレシピに乗っているとおりです
使い方:
末尾最適化してやりたい, 関数にDecoratorをつけるだけ
@tail_recursive def fib(n, p, val=1): return fib(n-1, val, val + p) if n > 0 else val fib(100) #>>> 573147844013817084101
仕組み解説:
デコレータによって関数中の再帰呼出しもラップされていることに 気をつければ後は簡単 func → wrapper → func →… という形で呼び出されることになります.
この時, wrapperは再帰的にfuncを呼ぶだすのではなく
再帰呼出しされた引数だけを, 記録してfuncを
新たに呼ぶことで再帰を避けています.
わかれば見事という他ないですね.
ただ書きなおしたほうが早い
結局のところ末尾再帰最適化の一番の方法は 以下のようにループで書きなおしてやることです
def fib(n): val, prev = 1, 1 for _ in xrange(n-1): val, prev = val + prev, val else: return val fib(100) #>>> 573147844013817084101
Vimでシンボリックを辿る
シンボリックリンクファイルの編集方法
シンボリックを編集するとき,皆さんどうしてますでしょうか?
vimではシンボリックリンクのファイルを編集するとき パスの展開は行われずに編集が開始されます.
僕はdotfilesをクローンしてきて, それをシンボリックリンクで
home以下においているのですが,
fugitiveを利用しようと思うとフルパスではないと行けないため, これが意外と面倒に思うわけです
本日はそんな人のためにシンボリックリンクをたどって ファイルを編集する方法を示します.
ファイルのフルパスを得て,編集を行う
現在のバッファについて, そのパスからシンボリックファイルをたどった パスを得るには以下のコマンドです
:echo resolve(expand('%'))
resolve, expandについては:h resolve等で確認してみてください.
シンボリックリンク先のファイルを編集したい場合は以下のコマンド
:e <C-R>=resolve(expand('%'))
便利なシンボリックファイルのリンク先編集コマンド
さっきのものをこれをいちいち入力するのは面倒ですね. .vimrcにコマンドとして以下の書いてしまいましょう
command! FollowSymlink call s:SwitchToActualFile() function! s:SwitchToActualFile() let l:fname = resolve(expand('%:p')) let l:pos = getpos('.') let l:bufname = bufname('%') enew exec 'bw '. l:bufname exec "e" . fname call setpos('.', pos) endfunction
これでシンボリックに煩わされる日々も無くなりました!
言語処理のための例外処理
経緯
私,自然言語処理に携わる研究を行っているのですが
必然と大規模なテキストデータを扱います.
(恐らく大半の言語処理のプログラムを書いている方に訪れるであろう)鬼門が
どれだけ網羅しようともテキストデータに例外が存在する
ということです.
自然言語処理の大半は,この例外と戦うのではないのかというぐらいに 例外が発生しまくります.
これを取り除くために前処理(の前処理)という形で
テキストのフィルタリング・整形を行う必要が出てくるわけです.
それでも潰しても,潰しても消えない例外は存在するわけでして…
ならば例外が起こることを考慮したプログラムを書くのが一番です.
ということで今回の記事を書くに当たりました.
コンテンツ
- 例外処理の書き方
- 例外を定義する
- デコレータを使った賢い例外処理
1.例外処理の基本
pythonにおける基本的な例外処理は以下のように書きます.
try: # 例外が発生すると思われる処理 pass except AttributeError: # AttributeError という例外が発生した時の処理 pass except ValueError as e: # ValueError という例外が発生した時の処理 # e に 例外の内容のインスタンスが格納される print e.args #例外生成時に使用される変数 print e.message #例外のメッセージ except (ZeroDivisionError, RuntimeError, TypeError): # 複数の例外に対して一括の処理を書きたい場合は # この書き方 except : #上でキャッチしきれなかった例外. 取り扱い注意 #エラーメッセージを送出した上で例外を更に投げるのに向いている. raise else : #例外が発生しなかった時にする処理 finally: #例外が起ころうが,例外が起ころまいが行う処理 pass
それぞれのブロック(except, else, finally)は必要に応じで省略可能です. 例外が発生した場合, 行っていた処理は中断されます.
exceptには様々な種類の例外があります. (自作できます. なので一番はライブラリの例外を確認することです)
2. 例外を定義する
自分のプログラムに合った例外を作りましょう. 作り方は簡単です. Exceptionクラスを継承したクラスを作成するだけです.
class MyError(Exception): pass #例外の定義はこれだけ def func(x): if x < 5: raise MyError(x) return x def main(): func(5) # -> 例外は発生しない func(2) # -> MyErrorが発生する
自作の例外は, 一つ継承用の例外を作り,
それを継承すると他の例外を区別しやすく保守も容易になります.
class MyError(Exception): pass #継承用の例外を用意する class SpecifiedError(MyError): pass #上記を継承する. class UnknownError(MyError): pass def func(x): if x < 5: raise UnknownError(x) return x def main(): try: func(5) except MyError as e: #MyErrorを継承しているので,これでUnknownErrorの例外も処理できる print e
3.デコレータを使った賢い例外処理
さて,実はここまではただの前置きです. 例外処理では例外が発生すると, 処理を中断してまいます.
これって意外と困った処理だったりします. 例えば以下のプログラム
def func(y): return 1./y def main(): try: results = map(func, range(1000)) except ZeroDivisionError: print '0除算は禁止です'
こんなプログラムを書くと,rangeの範囲は0~999ですので 当たり前ですが, ZeroDivisionErrorが発生します.
例外を捉えれるのはいいんですが, このプログラムだと, たった一つの例外(0)のために, 他の999個の処理の結果が失われるのです.
これってむちゃくちゃ非効率ですよね.
この非効率さを解決するには, もとのfunc関数をいじってやれば良いのでしょうが
これが公開されているライブラリとかになれば,かなり鬱陶しいことになります. そこで私がおすすめするのが以下の方法
def wrapper(func, x, *args, **keyword): try: return func(x) except ZeroDivisionError: return -1 @wrapper def func(y): return 1./y def main(): results = map(func, range(1000)) # resultsには1000個のデータが入ることが保証される print results
これだけで,かなり鬱陶しい操作がなくなるのでお勧めですよ!
PythonでGram-Schmidt(グラム-シュミット)正規直交化をする
pythonはC/C++と違って
何やるにしても十分にツールが充実しているのがすばらしいですね
ということで、今日はpythonでGram-Schmidt正規直交化を紹介します!
正規直交化っていうのは、いくつかのベクトル列から
正規直交(大きさ1, 直交なベクトルの内積は必ず0)なベクトル群を作り出す操作ですね。
大学生ならできて当然、線形代数でも超標準操作です.
(アルゴリズム的には, 最初のベクトルを正規化して
その成分を他のベクトルから引けば必然的に直交になるよねってことです)
詳細は割愛.
numpyでは一行でかけます.(numpy.linalgクラスに線形代数(linear algebra)操作のモジュールがまとまってます) numpyはQR分解ではGram-Schmidt正規直交化が使われるんですね。
import numpy as np mtx = np.array([[1,3],[0,5]]) q, r = np.linalg.qr(mtx) #qに正規直交化されたベクトルが格納される print q
pythonべんりー。numpyすげー
Vim Tips @004 markdown 中の code ブロックをハイライトする
編集中のcodeブロック内をハイライトしたい。
markdownを使っているなら思うはず。
たぶん方法はいくつかあるが自分が使ってるtpope/vim-markdownに
便利な機能がついていたのでそれを紹介する
設定はこれだけ
let g:markdown_fenced_languages = ['vim', 'python', 'ruby', 'javacript', 'cpp']
これでcode block内がハイライトされる.
tpopeさんすごすぐる。
ちなみにfoldingを可能にすることもできる
let g:markdown_folding = 1
Vim Tips @003 VimでCtrl+EnterやShift+Enterをマッピングする.
Ctrl+EnterやShift+Enterをmappingする.
<C-CR>や<S-CR>, <C-Enter>や<S-Enter> ,<C-Return>や<S-Return>
と記述があるが、まあ要はそこらへんのキーのマップをどうやって行うかの説明である.
ここらへんのマップは一筋縄じゃないかない.
GUIでは普通にできる
GVimとかMacVimならば普通に以下のコードでマッピングできる(はず).
innoremap <C-CR> rhs "rhsをCtrl+Enterにmappingする innoremap <S-CR> rhs "rhsをShift+Enterにmappingする
ターミナルだと普通にできない
できるやつもあるかもしれないが
ターミナルだとできない。詳しくは省略(:h xterm を見るといい)
しかもターミナルごとにキーの割り当てが違うので下記の方法を使う
非整数文字を直接mapする.
やり方は簡単で挿入モードで<CTRL-V>を押してから, Shitf+Enterなどを押す.
Cygwin環境では以下のような設定になる.
innoremap ^^ rhs "rhsをCtrl+Enterにmappingする (Cygwin上) innoremap ^^ rhs "rhsをShift+Enterにmappingする (Cygwin上)
Vim Tips @002 Vim から Hatena Blog に投稿を行う(hateblo.vim)
VimからHatena Blogへ記事を投稿する.
Hatena Blogはmarkdownで記事を記述することができる。
これが非常に便利であるのには違いないのだが、やはり書き慣れたエディタで記述したい。
そうVi)で記事を投稿したい。
そこで素敵なプラグインである hateblo.vimを紹介する.
ツールが充実しているサービスはよいサービスである.
hateblo.vimの紹介
mozinionさんが作成してくれた非常にありがたいHatenaBlogプラグインである.
NeoBundleならば以下のように設定しよう. webapi-vimとunite.vim依存である. uniteが使える素晴らしい.
NeoBundle 'mozinion/hateblo.vim' ,{ \ 'depends' : ['mattn/webapi-vim', 'Shoug/unite.vim'], \}
設定につていは READMEを参照していただきたいが 初期設定としてはホームディレクトリ下($HOME)に.hatena.vimファイルを下記のような内容で作成すればよい.
let g:hateblo_vim = { \ 'user': 'user_name', \ 'api_key': 'api_key', \ 'api_endpoint': 'http://.../atom', \ 'WYSIWYG_mode': 0 | 1, \ 'always_yes': 0 | 1, \ 'edit_command': 'edit' | 'tabnew' | 'split' | 'vsplit' | etc... \ }
利用方法は簡単、下記のコマンドがコマンドラインから使える.
Command | Description |
---|---|
:HatebloCreate | バッファを記事として投稿する |
:HatebloCreateDraft | バッファを記事を下書きとして投稿する |
:HatebloList | HatenaBlog中の記事をUniteで表示する |
:HatebloDelete | 記事を削除する |
hateblo.vimを自分好みに書き換えた
このままでもすばらしいプラグインには違いないのだが、
さらに欲を言えば記事の修正・投稿、管理するのにサイトにアクセスするのも面倒だし
私は記事は手元に置いておきたい(あとで検索等したいので)
設定ファイルの位置を変えたいのと、記事の保存場所としてローカルのパスを指定したかったので その設定の変更を行うため、hateblo.vimを修正した.
NeoBundle 'TKNGUE/hateblo.vim' ,{ \ 'depends' : ['mattn/webapi-vim', 'Shoug/unite.vim'], \} let g:hateblo_config_path = '$HOME/.hateblo/.hateblo.vim' let g:hateblo_dir = '$HOME/.hateblo/blog'
これでブログ投稿もできVimになったのである。