【一人 Ruby Advent Calendar 2017】キーワード引数や Hash の渡し方や受け取り方いろいろ【24日目】

一人 Ruby Advent Calendar 2017 24日目の記事になります。
Ruby 3.0 でキーワード引数をぶっ壊すらしいのですが、現状の仕様をよく理解していないので簡単にまとめてみようかと。

Hash を渡す時に {} を省略

引数の最後が Hash 値の場合、{} を省略して書くことが出来ます。

def func a, b
    return a, b
end

# Hash 値を渡す
p func 42, { name: "homu", age: 14 }
# =>[42, {:name=>"homu", :age=>14}]

# 最後の引数が Hash 値の場合、{} を省略出来る
p func 42, name: "homu", age: 14
# =>[42, {:name=>"homu", :age=>14}]

# Hash でなくても渡せる
p func 42, 32
# =>[42, 32]

# 最後でなければエラー
# Error:syntax error, unexpected ')', expecting =>
# p func name: "homu", age: 14, 42

** で受け取る

メソッドの引数に ** を付けることで『Hash として受け取る』事を明示化することが出来ます。

def func a, **hash
    return a, hash
end

# Hash 値を渡す
p func 42, { name: "homu", age: 14 }
# =>[42, {:name=>"homu", :age=>14}]

# 最後の引数が Hash 値の場合、{} を省略出来る
p func 42, name: "homu", age: 14
# =>[42, {:name=>"homu", :age=>14}]

# Hash でなければエラー
# Error: wrong number of arguments (given 2, expected 1) (ArgumentError)
p func 42, 32

キーワード引数

以下のように引数を定義する時に『引数名: デフォルト値』と記述することでキーワード引数として定義できる。
キーワード引数はメソッドを呼び出す時に『引数名: 値』のようにして『任意の引数名に対して』引数を渡すことが出来る。

def func id, name: :homu, age: 14
    { id: id, name: name, age: age }
end

p func 1, name: :homu, age: 14
# =>{:id=>1, :name=>:homu, :age=>14}
p func 1, age: 15, name: :mami
# =>{:id=>1, :name=>:mami, :age=>15}

よくあるのが複数の引数があった場合に『一部の引数だけ渡す』みたいな時に使うことが多いですかねー。

def setting a: 1, b: 2, c: 3
    
end

# 一部の引数だけ渡す
setting a: 10
setting b: 20
setting c: 30

まとめ

  • Hash をいい感じに渡す手段はいくつかある
  • メソッドの受け取り側と渡し側でどのようにして引数を渡すのか意識する必要がある


今まで雰囲気で使っていたんですが、思ったよりも役割によって使う機能が違っている感じですねー。
もっとなんか複雑だと思っていたんですが、言われてみれば『そうかーそうやって使い分けるのかー』という印象。
Ruby 3.0 だとこのあたりをもっとよくするらしいんですが、どうなるんでしょうねえ…。

参照

【一人 vimrc advent calendar 2017】キーマッピングの注意点とか【24日目】

一人 vimrc advent calendar 2017 24日目の記事になります。
今日はクリスマスイヴですね。
ケーキを買いに行ったら売り切れていましたかなしい。

モードごとのキーマッピング

さて、気を取り直して…。
Vim はモードごとに操作が分けられているのはご存知だと思いますが、キーマッピングもモードごとに設定することが出来ます。
代表的なのは以下のようなものになります。

登録コマンド 削除コマンド モード
:map :noremap :unmap ノーマル、ビジュアル、選択、オペレータ待機
:nmap :nnoremap :nunmap ノーマル
:vmap :vnoremap :vunmap ビジュアル、選択
:smap :snoremap :sunmap 選択
:xmap :xnoremap :xunmap ビジュアル
:omap :onoremap :ounmap オペレータ待機
:map! :noremap! :unmap! 挿入、コマンドライン
:imap :inoremap :iunmap 挿入
:lmap :lnoremap :lunmap 挿入、コマンドライン、Lang-Arg
:cmap :cnoremap :cunmap コマンドライン

いろいろとありますが、基本的には

  • :nmap :nnoremap
  • :vmap :vnoremap
  • :imap :inoremap
  • :cmap :cnoremap

の4つを覚えておけばよいです(あとは textobj 系プラグインを使うなら omap も。
キーマッピング自体の仕方はまあわかってると思うので省略。

mapnoremap の違い

Vim でキーマッピングをする上で重要なのが mapnoremap の違いになります。
と、言っても特に難しくはなくて map は『再マップされ』、noremap は『再マップされません』。
どういうことかというと例えば、

imap a aa

とした場合、a を入力した時に aa を更に展開しようとするので無限ループします。
一方、

inoremap a aa

の場合は aa は再展開されないので aa だけが入力されます。
なのでキーマッピングを行う際に『他に設定したキーマッピングの影響を受けない場合は :noremap』、『他に設定したキーマッピングの影響を受ける場合は :map』みたいにして使い分ける必要があります。
と、言っても意図的に展開してほしい場合以外は noremap を使っておくほうが混乱がないと思います。

ただし、プラグインを使用していると例えば、

nmap hoge <Plug>(hoge-plugin-mapping)

みたいに <Plug>(hoge-plugin-mapping) をキーマッピングすることがあります。
この <Plug>(hoge-plugin-mapping) というのは『プラグイン側でマッピングされている設定』になります。

" プラグイン内ではこんな感じでキーマッピングされている
nnoremap <silent> <Plug>(hoge-plugin-mapping) :<C-u>Hoge<CR>

なので、このような場合は mapマッピングする必要があります。

まとめ

  • Vim ではモードごとにキーマッピングを行うことが出来る
  • mapnoremap は再展開されるかされないかの違い
  • 基本的には noremap を使っておくほうが副作用がなくて安全


キーマッピングについてはいろいろと書きたいことはあるんですが、まー基本的には変な縛りを設けないで自分が使いやすいようなキーマッピングをしておくとストレスなく Vim を使うことが出来るのでよいと思います。
Vim に慣れるのではなくて、自分が使いやすい Vim を目指しましょう。

【一人 Ruby Advent Calendar 2017】Ruby で外部ファイルを読み込むメソッドいろいろ【23日目】

一人 Ruby Advent Calendar 2017 23日目の記事になります。
今回は require などの外部ファイルを読み込むメソッドを簡単にまとめてみます。

Kernel.#require

引数の Ruby のライブラリを読み込みます。
ここでいう Ruby のライブラリとは、

などのファイルを指します。

# Ruby のスクリプロファイルを読み込む
require "hoge.rb"

# 動的ライブラリを読み込む
require "foo.o"

また、拡張子を省略して記述することも出来ます(その場合は *.rb を優先して両方のファイルを検索します。

# hoge.rb を優先しつつ hoge.o などを読み込む
require "hoge"

同じファイルは複数回読み込みません。
ファイルが絶対パスの場合はそのファイルを、相対パスの場合は組み込み変数 $: から検索し、最初に見つかったファイルを読み込みます。
また、『require してるファイルのカレントディレクトリ』は検索の対象にならないので『require しているファイルからの相対パス』で読み込みたい場合は注意してください。

# こんな感じで require しているファイルからの相対パスでファイルは読み込めない
require "../hoge/foo"

Kernel.#require_relative

『現在のファイルからの相対パス』で require します。
以下のような挙動とほぼ同じです。

require File.expand_path({ファイル名}, File.dirname(__FILE__))

require しているファイルからの相対パス』のファイルを読み込みたい場合は require ではなくて require_relative を使用してください。

Kernel.#load

require と違い再ロード可能です。
相対パスの場合は『カレントディレクトリ』も含めて検索します。
また、require は拡張子を省略した場合は自動的に *.rb などを補完しますが、load は『拡張子を補完しない』ので拡張子を記述する必要が有ります。

Kernel.#autoload

指定した定数の名前を最初に参照した時に自動で require を行います。

# この時点ではまだ "hoge" は読み込まれない
autoload(:Hoge, "hoge")

# 最初に Hoge を参照した時に require("hoge") が実行される
p Hoge

まとめ

メソッド名 検索対象 拡張子 再ロード
require $: のパス 省略化 されない
require_relative 現在のファイルからの相対パス 省略化 されない
require $: とカレントディレクト 省略不可 される


基本的に『ライブラリを読み込む際は require』を使い、『相対パスで読み込みたい場合は `require_relative』を使う、みたいな感じで使い分けるとよいと思います。

【一人 vimrc advent calendar 2017】gvimrc について【23日目】

一人 vimrc advent calendar 2017 23日目の記事になります。
そういえば、gvimrc について書いてなかったので簡単に書いてみようかと。

GUIVim

GUI 版の Vim を起動するには環境にもよりますが gvim コマンドを使って起動させる事が出来ます。
これにより端末上ではなくて GUI アプリとして Vim を起動させることが出来ます。
gvimrc ファイルは vimrc ファイルと同じように起動時に読み込まれる設定ファイルですが、GUI 版の Vim を起動した時に『vimrc の後』に読み込まれます。
読み込まれる gvimrc ファイルは基本的に vimrc ファイルと同じですが、起動オプション -U {gvimrc}$MYGVIMRC で指定することが出来ます。

ユーザーの個人的なGUI初期化ファイルを置くのに推奨される場所:
    Unix            $HOME/.gvimrc、$HOME/.vim/gvimrc
    OS/2            $HOME/.gvimrc、$HOME/vimfiles/gvimrc、$VIM/.gvimrc
    MS-DOSとWin32   $HOME/_gvimrc、$HOME/vimfiles/gvimrc、$VIM/_gvimrc
    Amiga           s:.gvimrc、home:.gvimrc、home:vimfiles:gvimrc、
                    $VIM/.gvimrc

gvimrc で設定するべきオプション

当たり前ですが基本的には GUIVim でのみ有効にしたい設定を記述します。
また、GUI 版だけで意味のあるオプションもあるので、GUI 版の Vim を使う場合は調べてみるとよいと思います。

オプション 内容
'guicursor' カーソルの外観を指定
'guifont' GUI フォントの設定
'guipty' shell で pseudo-tty を使用するかどうかの設定
'guioptions' メニューやスクロールバーなど GUI 固有の設定

ちなみにこれらのオプションは vimrc で設定しておいても問題ないです(GUI 版でのみ有効になる。

まとめ

  • gvimrcGUI 版で読み込まれる設定ファイル
  • vimrc のあとに gvimrc が読み込まれる
  • gvimrc 固有の設定もある


端末上ではなくて GUI 版で Vim を使ってみたい方は試してみるとよいと思います。

【一人 vimrc advent calendar 2017】タブページを操作するキーマッピングいろいろ【22日目】

一人 vimrc advent calendar 2017 22日目の記事になります。
もうすぐ Advent Calendar も終わりですね…。

タブページを操作するキーマッピングいろいろ

さて、わたしは基本的にタブページごとにファイルを開いているのですが、そのタブページを扱うキーマッピングをいろいろと紹介してみます。

" タブの移動
nnoremap <silent> <C-l> :tabnext<CR>
nnoremap <silent> <C-h> :tabprevious<CR>
nnoremap <silent> <C-Tab> :tabnext<CR>

" タブページ自体を左右に移動させる
command! -bar TabMoveNext :execute "tabmove " tabpagenr() % tabpagenr("$") + (tabpagenr("$") == tabpagenr() ? 0 : 1)
command! -bar TabMovePrev :execute "tabmove" (tabpagenr() + tabpagenr("$") - 2) % tabpagenr("$") + (tabpagenr() == 1 ? 1 : 0)

nnoremap <silent> <S-l> :TabMoveNext<CR>
nnoremap <silent> <S-h> :TabMovePrev<CR>

上記のようなキーマッピングを行うことで素早くタブページを移動したりタブページのレイアウトを変更したり出来ます。

おまけ

:help setting-guitablabel にも書かれているんですが、以下のように 'guitablabel' を設定しておくと更にタブページが見やすくなります。

function GuiTabLabel()
  let label = ''
  let bufnrlist = tabpagebuflist(v:lnum)

  " このタブページに変更のあるバッファがるときには '+' を追加する
  for bufnr in bufnrlist
    if getbufvar(bufnr, "&modified")
      let label = '+'
      break
    endif
  endfor

  " ウィンドウが複数あるときにはその数を追加する
  let wincount = tabpagewinnr(v:lnum, '$')
  if wincount > 1
    let label .= wincount
  endif
  if label != ''
    let label .= ' '
  endif

  " バッファ名を追加する
  return label . bufname(bufnrlist[tabpagewinnr(v:lnum) - 1])
endfunction

set guitablabel=%{GuiTabLabel()}

タブページを使うと視覚的にもわかりやすいのでバッファがよくわからんという人は使ってみるとよいと思います。

【一人 Ruby Advent Calendar 2017】Ruby の % 記法【22日目】

一人 Ruby Advent Calendar 2017 22日目の記事になります。
そろそろラストスパート

% 記法

Ruby には % 記法という特別なリテラルがあります。
例えば、%w という記法を使うと以下のように『空白文字で分割した』文字列の配列として定義されます。

p %w(homu mami mado)
# => ["homu", "mami", "mado"]

またこの時に記述した %w()()[]{}<> みたいな括弧や『改行を含めた任意の非英数字』を利用することが出来ます。

p %w<homu () mado>
# => ["homu", "()", "mado"]

p %w`'() [] {}`
# => ["'()", "[]", "{}"]

p %w!` " '!
# => ["`", "\"", "'"]

こんな感じでリテラルで定義する記号によって () の記号を変えることが出来ます。

%%Q:ダブルクオート文字列

%%Qは『ダブルクオート文字列』として定義されます。

p %(homu "mami")
# => "homu \"mami\""

# 式展開も行える
p %Q("#{5 + 4}:mado")
# => "\"9:mado\""

%q:シングルクオート文字列

%q は『シングルクオート文字列』として定義されます。

p %q(homu "mami" 'mado')
# => "homu \"mami\" 'mado'"

# 式展開は行われない
p %q(#{2 + 4})
# => "\#{2 + 4}"

%x:コマンド出力

%x は外部コマンドを実行して結果を返します。

p %x(ls)
# => ls コマンドの実行結果

` と同じです。

%r正規表現

%r は『正規表現リテラル』として定義されます。

p %r(\d+)
# => /\d+/

# / もエスケープされる
p %r(http://.*)
# => /http:\/\/.*/

%w:空白文字で区切った要素が文字列の配列を定義

%w は『空白文字で区切った要素が文字列の配列』を定義します。

p %w(homu mami mado)
# => ["homu", "mami", "mado"]

p %w("homu" "ma mi" "mado")
# => ["\"homu\"", "\"ma", "mi\"", "\"mado\""]

%W%w と同等(式展開とバックスラッシュ記法が有効)

%w と同等ですが『式展開とバックスラッシュ記法』が有効です。

p %W(#{2 + 4}:#{[1, 2, 3]})
# => ["6:[1, 2, 3]"]

p %W(homu ma\mi mado)
# => ["homu", "mami", "mado"]

%s:シンボル

%s は『シンボル』で定義されます。
また、式展開、バックスラッシュ記法は無効です。

p %s(homu)
# => :homu

p %s(42)
# => :"42"

p %s(#{1 + 2})
# => :"\#{1 + 2}"

%i:空白文字で区切った要素がシンボルの配列を定義

%i%w と同様に『空白文字で区切った要素』の配列として定義されますが、文字列ではなくてシンボルとして定義されます。

p %i(homu mami mado)
# => [:homu, :mami, :mado]

p %i(homu mami #{2 + 2})
# => [:homu, :mami, :"\#{2", :+, :"2}"

%I%i と同等(式展開とバックスラッシュ記法が有効)

%i と同等ですが、『式展開とバックスラッシュ記法』が有効になります。

p %I(homu mami mado)
# => [:homu, :mami, :mado]

p %I(homu mami #{2 + 2})
# => [:homu, :mami, :"4"]

まとめ

  • % 記法を使って様々な値を定義することが出来る
  • 文字列の配列を定義する場合は %w を使うと楽
  • 正規表現/ を提議したい場合は %r を使うとエスケープする必要がない


正規表現/ を含めいたことは稀によくあるのでそういう場合は %r を使うと楽ですねえ。

参照

【一人 vimrc advent calendar 2017】コマンドを実行した際にカーソル位置を移動させないスクリプト【21日目】

一人 vimrc advent calendar 2017 21日目の記事になります。
そろそろネタが枯渇してきているのですが、今日 vimrc から便利そうなスクリプトを発掘したのでそれの初回でも。

カーソル位置などを移動させないでコマンドを実行するスクリプト

元々、他の方の vimrc に書かれていたスクリプトになります。

" 元ネタ
" https://github.com/bling/dotvim/blob/d84e501bd12f9c6887599ace398fe26f542e8f6e/vimrc#L105
function! s:preserve(command) "{{{
   " preparation: save last search, and cursor position.
    let _s=@/
    let view = winsaveview()
   " do the business:
    execute a:command
   " clean up: restore previous search history, and cursor position
    let @/=_s
    call winrestview(view)
endfunction "}}}

command! -complete=command -nargs=*
\   Preserve call s:preserve(<q-args>)

上記スクリプトを使うことで例えば :substitute した時などに『カーソル位置を移動させずに』置換を行うことが出来ます。

:Preserve %s/\s\+$//g

また、以下のようなコマンドを定義しておくと :Preserve %s:S として使えるようになります。

command! -complete=command -range -nargs=*
\   S Preserve %s <args>

今までほとんど使ったことがなかったんですが普通に便利そう…。