Igor Pro コーディング規約

Thomas Braun氏によってIgor Exchangeに投稿されているIgor Proのコーディング規約(非公式)が,簡潔で分かりやすかったので本人の許可を得て翻訳しました. ドキュメント作成ソフトのDoxygen等,日本のIgor Proユーザーにはおそらく馴染みのないものもありますが,規約の多くの部分がコーディングの参考にできると思います.

原文:

http://www.igorexchange.com/project/CodingConventions

https://github.com/t-b/igor-pro-coding-conventions

1 プロシージャ

  • コードはIgor Proの外部のプロシージャファイルに直接,書き込むべきです.

  • プロシージャファイル名はアルファベットの大文字小文字,アンダースコア,ハイフンのみで構成され,.ipfで終わります.

  • ファイルのエンコーディングはOSに依存します.しかし使用する文字は常にASCII文字に限られるべきです.Igor Pro 7でだけ使用するコードでは,テキストエンコーディングとしてUTF-8を用い#pragma TextEncoding = "UTF-8"を指定します.

  • プロシージャファイルは#pragma rtGlobals=3およびその説明コメント*1で開始されます.

  • 改行コードは常にUNIXスタイル(LF)を用いるべきです.

2 空白文字とコメント

コメント

  • ファイル,関数,マクロ,定数の説明(ドキュメント)にはdoxygenを用います.

  • 次のように,末尾のコメントの前には常に一つの空白文字を置くべきです.

if (a < 0)
  b = 1
else // positive numbers (including zero)
  b = 4711
endif
  • コメントを行末に付すよりは,行を分けて書くべきです.

Doxygen

  • doxygenのコメントは///で開始します.また,定義文の末尾で説明を加えるには///<を使うべきです*2

  • @paramキーワードに続けて引数の名前と説明を書く場合,引数の説明は関数に渡す順番に通りに並べるべきです.

/// @param pressure Pressure of the cell
/// @param temperature Outdoor temperature
/// @param length Length of a soccer field
Function PerformCalculation(pressure, temperature, length)
    variable pressure, temperature, length // code
End
  • 関数が引数の値渡しと参照渡し*3の両方を使うとき場合は,in/out指定を@paramに付けるべきです.
/// @param[in] name Name of the device
/// @param[out] type Device type
/// @param[out] number Device number
Function ParseString(name, type, number)
    string name
    variable &type, &number
// code
End
  • オプション変数の説明は次のようにします.
/// @param verbose [optional, default = 0] Verbosely output
///                                        the steps of the performed calculations
Function DoCalculation([verbose])
    variable verbose // code
End

空白文字

  • 各関数は他のコードから改行1行分だけ隔てて書くべきです.

  • インデントにはタブ文字を用い,(Igor Pro以外のソフトウェアでコーディングしていてタブ文字が使えない場合は)タブ文字には4つの空白文字を当てるべきです.

  • 行を分けてコメントする場合,コメントのインデントの深さは周囲のコードに合わせます.

  • 関数の引数の宣言,ローカル変数の宣言,関数の残りの部分はそれぞれ改行で隔てて書きます.

Function CalculatePressure(weight, size)
    variable weight, size

    variable i, numEntries

    // code
End
  • 数学演算子,2進数演算子,比較演算子および代入記号の前後にはひとつの空白を書きます.また,コンマ,セミコロンの後にも空白ひとつを書きます.
a = b + c * (d + 1) / 5

if(a < b)
    c = a^2 + b^2
end

Make/O/N={1, 2} data
for(i = 0; i < numWaves; i += 1)
    a = i^2
endfor

if(myStatus && myClock)
    e=f
endif
  • 行の末尾の余分な空白を避けるように心掛けてください.以下,空白文字を␣で,タブ文字を⇥で表します.
良い例:
Function␣DoStuff()
⇥print␣"Hi"

⇥if(a␣<␣b)
⇥⇥c␣=␣a^2␣+␣b^2
⇥end

⇥Make/O/N={1,␣2}␣data
End

悪い例:
Function␣DoStuff()␣
⇥print␣"Hi"␣⇥
⇥
⇥if(a␣<␣b)␣␣␣␣
⇥⇥c␣=␣a^2␣+␣b^2⇥
⇥end␣
⇥
⇥Make/O/N={1,␣2}␣data␣
End
  • if/endiffor/endfordo/whileswitch/endswitchのようなコードブロックがプログラムのロジックを表している場合には,前後には改行を入れます.
for(i = 0; i < numEntries; i += 1)
    // code
endfor

if(a > b)
    c=d
elseif(a == b)
    c=e
else
    c=0
endif

switch(mode)
    case MODE1:
        a = "myString"
        break
    case MODE2:
        a = "someOtherString"
        break
    default:
        Abort "unknown mode"
        break
endswitch

以下のような場合は,直前の代入文がロジックの一部なのでifforの前に改行を入れません.

numEntries = ItemsInList(list)
for(i = 0; i < numEntries; i += 1)
    // code
endfor

NVAR num = root:fancyNumber
if(num < 5)
    // code
endif

複数のend文が続く場合には,改行を省略します.

for(i = 0; i < numEntries; i += 1)
    // code

    if(i < 5)
        // code
    endif
endfor
  • 操作関数のフラグの間には空白を入れません.また,フラグへの代入を行う場合の=の前後にも空白を入れません.
良い例:
Wave/Z/T/SDFR=dfr wv = myWave

Function/S DoStuff() // code

End
悪い例: 
Wave /Z /T /SDFR = dfr wv = myWave
  • 引数の参照渡しに使う&は変数名の直前に置きます.
良い例:
Function DoStuff(length, height, weight)
    variable &length, &height, &weight
    
    // code
End

悪い例:
Function DoStuff(length, height, weight)
    variable& length, & height,& weight
    
    // code
End

3 コード

3.1 基本

  • 1行は80文字を超えないようにします.

  • variable/string/wave/dfrefの名前にはcamelCaseを使い,構造体の名前にはCamelCaseを使います.

  • GUIをコントロールするためのプロシージャには,古いスタイルの関数より構造体を用いた関数を使うべきです.

  • i, j, k, lという名前の変数はループカウンターとしてだけ使うべきです.後者ほど内側のループで使われます.

  • 一時的に値を格納するウェーブとして,フリーウェーブを使うべきです.

  • できる限りSetDataFolderを使わずにコードを書くべきです.関数が呼ばれるときに特定のデータフォルダをカレントフォルダとしていることを期待するなら,適切なドキュメントを書いておくべきです.関数中でカレントフォルダを変更した場合は常に,関数の終了前にカレントフォルダをもとに戻します.

  • Igor Proのコードは大文字小文字の違いを問題にしませんが,可読性の向上のため,Igor Proのヘルプファイルに書かれている公式の名称に合わせて大文字小文字を書き分けるべきです.

Make/N=(10) data
AppendToGraph/W=$graph data
WAVE/Z wv
SVAR sv = abcd
STRUCT Rectangular rect
print ItemsInList(list)

ただし,以下のふたつの場合は例外です*4

variable storageCount
string name
  • 変数や関数を定義するときや,それらを使うときには,大文字小文字の使い方を変えてはいけません.

  • 関数の戻り値とする値を,一時的に格納するために変数を使うべきではありません.

良い例:
if(someCondition) // code
    return 0 else
    // code
    return 1
endif
// 関数の戻り値が状態であることを知らせたいのなら,
// 関数名をGetStatusForFooのようにするか.doxygenのコメントとして@returnを使うべきです.
// あるいは,関数名とdoxygenコメントの両方を使うべきです.

悪い例:
variable status

// code

if(someCondition) // code
    status = 0
else
    // code
    status = 1
endif

return status
  • コメントアウトされたコードを残しておくべきではありません.

  • その必要がないなら,数値変数や文字列変数を初期化するべきではありません.もし変数の初期化を行うなら,それぞれ別の行で行うべきです.

良い例:
variable i = 1
variable numEntries, maxLength
string list

悪い例:
variable i = 0, numEntries = ItemsInList(list), maxLength
string list = ""
  • 関数のオプション変数としてデフォルト値を使うなら,それをわざわざ書くべきではありません.
良い例:
StringFromList(0, list)

悪い例:
StringFromList(0, list, ";")
  • 括弧は省略できるのなら省略するべきです.
良い例:
variable a = b * (1 + 2)

if(a < b || a < c)
    // code
endif

悪い例:
variable a = (b * (1 + 2))

if((a < b) || (a < c))
    // code
endif
  • 優先度が等しい演算子を組み合わせるときには,括弧を使うべきです.
良い例:
if((A || B) && C)
    // code
endif

if(A == (B >= C))
    // code
endif

悪い例:
if(A || B && C) // same as above as these are left to right
    // code
endif

if(A == B >= C) // same as above as these are right to left
    // code
endif

正確な結合則を記憶しておくのは難しく,エラーを起こしやすくなるからです. DisplayHelpTopic "Operators"も参照してください.

3.2 定数

  • あるファイルの内部でだけ有効なstatic定数は,そのファイルの先頭で定義します.
  • グローバル定数の名前はアルファベット大文字とアンダースコアのみで構成します,グローバル定数の定義はただ一つのファイルに集約されるべきです.
  • マジックナンバー*5にはコメントで説明をつけるべきです.
    static Constant DEFAULT_WAVE_SIZE = 128 // equals 2^8 which is
                                            // the width of the DAC signal

3.3 マクロ

  • マクロはウィンドウ再構成マクロとしてだけ使うべきです.
  • ウィンドウ再構成マクロを自分の手で書き換えるのは避けるべきです.パネルをデフォルトの状態に戻したいのなら,関数中でDoWindow/Rを呼び出し,Igor Proにマクロを書き換えさせるべきです.

3.4 関数

  • 関数の長さは50行(あるいは画面の高さの半分)に収めるようにします.

  • 関数名にはCamelCaseを用います(ファイル名を表すためにSomeString_といった接頭語を付けることは認められます).

  • 関数がそのプロシージャファイルの内部でしか使われないのなら,それらの関数はstaticにします*6

  • 次のように,すべての変数を関数の初めの部分で定義します.

Function CalculatePressure(weight, size)
    variable weight, size
    
    variable i, numEntries

    // code
End

これは,Igor Proではブロックスコープが存在しないからです*7.すなわち

if(someCondition)
    variable a = 4711
end

print a

は正しいコードです.これはC/C++経験者を混乱させます.

4 リンク・文献

*1:訳注:新しいプロシージャウィンドウを作成したときに書いてある#pragma rtGlobals = 3 // Use modern global access method and strict wave access.を消すべきではないということ.

*2:訳注:Variable num ///< number of points のように,変数定義等の直後にその説明を置く場合

*3:訳注:例のように,変数名の頭に&を付けて引数を受け取ると,関数内での変数値の変更が関数の呼び出し元にも反映されるようになります.この機能を変数の参照渡しと呼びます.

*4:訳注:Igor ProヘルプファイルではVariable,Stringは大文字で始まります.この例外はおそらく,組み込み型が小文字で書かれるC/C++の慣習に合わせたものでしょう.

*5:訳注:プログラムに現れる,ぱっと見て意味の分からない数字のこと.

*6:訳注:関数定義をFunctionのかわりにstatic Functionで始めると,プロシージャファイルの外からその関数が使えなくなります.ただし,#pragma ModuleName=SomeStringによってモジュール化するとstaticな関数をファイルの外からも使うことができるようになります.

*7:訳注:if/endifブロックの内部で定義した変数はブロックの内部でだけ有効(ブロックスコープ)だと便利ですが,Igor Proではそうなっていない,ということ.

Igor Pro上のシェル,CommandPanelをちょっとアップデート

以前紹介したIgor Pro上のシェルことCommandPanel,ちょこちょこアップデートしていたので改善点の報告など.

github.com

Igor Pro 7でも起動できるようになった

以前のバージョンは,Igor Pro 7で起動するとそれだけで無限ループに陥ってしまっていました. 新しいバージョンはIgor Pro 7でも起動できます.

しかし,ShiftEnterの同時押しが認識されないらしく,コマンドの補完やバッファの絞り込みができません.

ウィンドウの描画が改善された

ウィンドウサイズを変更した際,ウィンドウフック関数を用いてコマンドラインやバッファの再描画をするようにしました.

古い版ではコントロールアクションを利用していたので,ウィンドウサイズ変更に対してコントロールの描画が一拍遅れていました.

テストを作成した

以前のエントリで紹介したテスト関数を用い,テストを作成しました.

ブレース展開が速くなった

テストの作成と合わせてリファクタリングを行い,とくにブレース展開の速度を改善しました.

以前は

print {1..500}

などとするとかなり待たされたのですが,我慢できるレベルになった気がします.ただbashが一瞬で処理してくれるのを考えると今でも相当遅いので,そのうちなんとかしたいです.

標準のフォントがIgor Proの標準フォントになった

標準のフォントをArialからIgor Proの標準のフォントに変更しました.日本語版Igor Proを使っている方は,とくに設定せずに日本語文字が表示されます.

パス名の補完・展開で上位のフォルダを参照できるようになった

Igor Pro内部での相対パス指定では,::がひとつ上のフォルダ,:::がふたつ上のフォルダを表します.これらの上位のパスを参照する書き方をパス名補完とパス名展開が認識するようになりました.

バッファの見た目と中身を別々に指定できるようななった

詳細はwikiの該当箇所を見ていただくとして,具体的には次のような関数が定義できるようになりました.

https://github.com/ryotako/igor-CommandPanel/wiki/CommandPanel_Helps.gif

バッファの全ての要素に共通するHelpDisplayTopicを無視して,disで絞り込みができていることに注目してください.この関数,結構便利度高い気がします.

関数定義も,以下のようにそれほど複雑ではありません.

#include "CommandPanel"

Function Helps()
    String list = FunctionList ("*", ";", "KIND:1") + OperationList("*", ";" ,"")
    Variable n = ItemsInList(list)

    Make/FREE/T/N=(n) word = StringFromList(p, list)
    Make/FREE/T/N=(n) buffer = "[DisplayHelpTopic] ¥"" + word +"¥""
    Make/FREE/T/N=(n) line = "DisplayHelpTopic ¥"" + word + "¥""

    Sort word, word, buffer, line

    CommandPanel_SetBuffer(word, line = line, buffer = buffer)
End

Igor Proの最小構成のテスト関数群を書いた

はじめに

Igor Proのプロシージャを書いていると,改良したつもりが既存の機能を壊してしまっていた,ということがよくあります. よくありますがよくないです.何より「改良が怖い」というのが精神衛生上とてもよくないです.

で,調べたところソフトウェア開発には「単体テスト」なる技法がある模様. 関数の振る舞いをチェックするテストコードを用意しておき,変更を行うたびにそのテストがパスすることを確認するのだとか.

よし,それじゃあ(よく分かってないけど)Igor Proでも単体テストしよう!と思い立ち,単体テスト用のプロシージャを探したところ次のふたつを見つけました.

github.com

github.com

上記2つのプロシージャはどうも

を参考にして作られているようです.汎用プログラミング言語のテスティングフレームワークを参考にしていて高機能なのですが,単純な手続き型のIgor Proには機能過多かなぁ...と思いました. そもそもIgor Proユーザにしめるプログラマの割合ってそれほど大きくないはずで,「既存のテスティングフレームワークに似ている」はあまりメリットでもない気がしました.

なので,Igor Proに必要な,ミニマルな単体テスト関数群を書きました.

github.com

使い方

MinTest.ipfを読み込むと,以下の4つの関数が使えるようになります.

  • eq_var
  • eq_str
  • eq_wave
  • eq_text

publicな関数はこれだけ*1.ミニマルです.以下のようにTestで始まる関数を定義して,その中で使います.

Function TestDemo()

  eq_var(word_count("it is a test"), 4)
  eq_str(hello_world(), "Hello, world!")
  eq_wave(fibonacci(5), {1,1,2,3,5})
  eq_text(fizzbuzz(5), {"1","2","Fizz","4","Buzz"})

End

Function word_count(s)
    String s
    return ItemsInList(RemoveFromList(" ", s, " "), " ")
End

Function/S hello_world()
    return "Hello, world"
End

Function/WAVE fibonacci(n)
    Variable n
    Make/FREE/N=(n) w = 1
    w[2,inf] = w[p-1] + w[p-2]
    return w
End

Function/WAVE fizzbuzz(n)
    Variable n
    Make/FREE/T/N=(n) w = Num2Str(p+1)
    w = SelectString(mod(p+1,  3), "Fizz", w)
    w = SelectString(mod(p+1,  5), "Buzz", w)
    w = SelectString(mod(p+1, 15), "FizzBuzz", w)
    return w
End

ただこれだけだと,単体テストを知っている方からツッコミが来そうです. 単体テストはコードを改変するたびに実行することが大事で,自動実行できるようにしておくべきなのだ,比較用の関数だけ用意しても意味ないのだ,と.

MinTest.ipfでもテストの一括実行はサポートしていて,どこから行うかというとメニューバーを使います.

f:id:ryotako:20161204143315p:plain

Igor ProはもともとGUIのソフトなのでメニューバー使ってしまうことにしました.で,実行した結果がこちら.

f:id:ryotako:20161204143515p:plain

...失敗しました.テストの結果は履歴エリアに表示されるとともに,メニューバーの表示が変化します. 履歴エリアには期待された値(want),と実際に受け取った値(got)が表示されます.また,メニューバーのjump欄をクリックすると,テストコードの失敗した箇所にジャンプできます*2

hello_world()関数を修正してテストをパスするとこうなります.

f:id:ryotako:20161204144247p:plain

おわりに

テスト便利です.いちいちテスト書くの面倒そう...と思ってましたが,コード修正後の安心感は非常に大きいです. Igor Proはデータ解析用ソフトなので,関数にバグが有ると図らずもデータを歪めてしまうことになります. 学術分野で使われるコードにこそ,テストが必要な気がします.

余談

フィボナッチ数列の実装を見るとIgor Proがすごくデキる子に見える.代入がそのままmapなのが強力.

当然ですが代入をMultiThreadにして高速化しようとすると失敗します.

Function/WAVE fibonacci(n)
    Variable n
    Make/FREE/N=(n) w = 1
    MultiThread w[2,inf] = w[p-1] + w[p-2]
    return w // -> {1,1,2,2,4, ...}
End

*1:Igor Proの内蔵エディタの編集機能は乏しいので,できるだけ短い名前にしました.

*2:Igor Pro 6だと内臓エディタが行番号を表示できないので地味に便利.

実験系研究者がVimを使うと幸せになれる10の理由

はじめに

この記事はVimアドベントカレンダー2016の3日目の記事です.

先日はrhysdさんでピュアVim scriptのCコンパイラを作る話でした. 変態すぎ凄すぎてちょっと意味が分かりません.

昨日とは一転,本日担当の私はプログラミングが本職でないゆるいvimmerです.というか,専攻は情報系でさえなく実験物理をやっています. プログラマでなくてもvimを使うと幸せになれるよ,というのが本記事の趣旨になります.

vimガチ勢の方には,ゆるいvimmerはそんな視点でvimを選んでいるのねと,生暖かく見守っていただければ幸いです.

10の理由

1. 研究室は独自記法の宝庫

手を動かして実験する研究者でも,プレーンテキストに触れる機会は多いものです.具体的には

  • 測定機器の設定ファイルや,測定を自動化するためのシークエンスを書いたファイル
  • 測定データそのもの
  • 解析ソフトの設定ファイルや,解析のためのスクリプト
  • 論文を書くためのLaTeXのファイル
  • 先輩が残していったソフトウェアのための,奇怪な書式の入力ファイル

等です.これらをまとめて扱うにはメモ帳は明らかに力不足で,何らかの高機能テキストエディタの導入が必要になります.

2. 実験に使うWindowsでも,解析に使うMacでも動く

実験系の研究者の場合,実験を行ってその結果を解析する必要があります. で,実験装置の制御ソフトはメーカーからWindows版が提供されることが多いのに対して,便利な解析ソフトはMac(unix)で動くものが多いという事情があります*1

つまり,エディタもWindowsでもMacでも同じように動くものだととても便利です.

3. 専門用語の入力はGoogle IMEにおまかせ

どのOSでも動く高機能なテキストエディタ,というとvimemacsが二強なわけですが,私がvimを選んだ理由のひとつにIMEとの連携がありました.どうもemacsはその内部に独自のIMEを持っているらしく,外部のIMEが使えません*2

私の場合,研究に関する文書を書くときには,例えば「しゅれ」の変換候補に「シュレーディンガー」を出してくれるGoogle日本語入力が手放せないため,emacsではなくvimを使うことに決めました.

4. キーバインドが覚えやすい

暗記科目が苦手だったから理系を選んだって話,自分の周りでは結構聞く気がします. 高機能エディタを使っても,機能が覚えられなければ意味がないのですが,vimには「モード」の概念があるため,キーバインドが比較的覚えやすいです. 少なくとも,ControlとAltとコレを同時に押すと...といったパターンはありません.

ただし,カーソルの移動がhjklなのは敷居が高い気がします*3

5. 最近は実験家も理論計算する

技術の進歩により,最近は実験家でも比較的簡単に理論計算ができるようになりました. 量子計算用のQuantum-ESPRESSO等,オープンソースの解析ソフトも多数開発されています.

ただ,こういった学術用のソフトはユーザー数が少ないため,多くのエディタではシンタックスハイライト等の対応がなされていません.しかしvimであれば,emacsと並んで最も広く使われているエディタであるので,誰かが専用の設定を書いてくれている公算が高いです.前記のQuantum-ESPRESSOであればこちら.

github.com

6. 強力なLaTeXサポート

実験系にかぎらず,理系研究者なら避けては通れないプレーンテキストファイルに,論文執筆のためのLaTeXファイルがあります. PDFビューアの設定に若干の面倒臭さがあるものの,例えばこちらのプラグインを使うことで,強力なLaTeX用エディタを手に入れることができます.

github.com

7. プレーンテキストで簡単ドキュメント

実験を安全にスムーズに行うため,実験装置には使い方のマニュアルがついているべきです.ただ,実験装置に付属のマニュアルは分厚すぎて参照性が低かったり,装置の魔改造によって本来の使い方と異なる手順が必要になっていたりします. しかし厄介なことに,そういった装置の使い方は先輩から口伝で教え継がれていたりします.

vimに限った話ではないのですが,プレーンテキストだとmarkdown記法を用いて簡単にマニュアルが作れます.見た目を整えたかったり,ボスから「Wordでくれ」と言われた場合には,pandocを使って(Wordを使わずに)プレーンテキストから.docxファイルが生成できる時代になってます.(詳細は以下の記事を参照してください.)

qiita.com

8. 設定を簡単に持ち運べるので出張でも安心

実験家であれば,共同実験先を訪問したり学会発表しに行ったりと,出張の機会も多々あるはずです. vimのカスタマイズには.vimrcというプレーンテキストファイルを使うので,これさえどうにかして*4持ち運べば,研究室のパソコンと同じ環境が出張用ノートパソコンに簡単に再現できます.

9. 安心の日本語マニュアル

以上が,実験家でも高機能のテキストエディタを使うと幸せになれる!といったポイントなのですが,高機能なソフトを使うときの不安として「使いこなすのが大変なのではないか?」というものがあります.ご安心ください.vimのマニュアルは日本語化されています.

github.com

10. コミュニティが活発

マニュアルを見ても使い方がよく分からない場合や,そもそもマニュアルのどこを見ればよいかわからない場合には?

日本のvimコミュニティは非常に活発なので,ググると大概回答が(日本語の記事として)見つかります.というか前記のヘルプ日本語化もvimコミュニティのお仕事で,大変ありがたいです.teratailのような質問サイトでも,質問すれば教えてもらえるはずです.

また,前述の.vimrcという設定ファイルの読書会(vimrc読書会)が毎週開かれているので,ぜひ覗いてみるのをおすすめします.便利な設定や,やってしまいがちな危ない設定等の貴重な情報が得られます*5

https://pbs.twimg.com/media/Cv80cd1UIAEqi4t.png

おわりに

記事の途中でも触れましたが,hjklでカーソル移動,という謎の独自の操作に慣れてしまえば,vimは大変便利なエディタです.プログラマーほどテキストに向き合う時間が多くない実験系研究者でも,充分その恩恵に与れます.

今回の記事でvimに興味を持つ研究者が少しでも増えれば幸いです.

*1:実験も解析もWindowsで完結する分野もそれなりにある気はします

*2:当時は知らなかったのですが,専用のパッチを当てた版では外部IMEが使えるようです.

*3:emacs使いへの変化が二次相転移なのに対して,vimmerへの変化は一時相転移なのだ,とか個人的には思ってます.で,vimを使うようになった後で,他のテキストエディタを触ったときに感じるもどかしさがヒステリシス.

*4:Dropboxのようなクラウドストレージを使うとか,githubリポジトリを作っておくとか

*5:最近ロゴができたようです.カッコイイ

Igor Proで関数をメニューバーから呼び出す.たった3行で

はじめに

Igor Proで処理を自動化する際には①マクロを書く,②関数を書く,の二種類の方法があります. 現在では,高速に動作する,コンパイル時にエラーチェックが入る,といった理由でマクロより関数の利用が推奨されているようです*1

ただ,関数と比べたマクロの利点として,マクロを定義するとメニューバーの「マクロ」欄に名前が表示されてワンクリックで実行できるようになります.

関数も,マクロみたいにメニューバーから使えるようにならないかな? と思ったのが今回の動機です.

結論: 3行でOK

f:id:ryotako:20161123094421p:plain

調べてみたところ,思った以上に簡単に実現できることがわかりました. 以下の3行をどこか*2に書くと引数なしのユーザー定義関数がメニューバーから実行できるようになります.

Menu "Functions"
    FunctionList("*",";","KIND:2,NPARAMS:0")
End

メニューに;区切りのリストを渡すと複数のメニュー項目に展開されることを利用しています.KIND:2は(組み込み関数ではなく)自作関数の指定,NPARAMS:0は引数なしの関数の指定です. FunctionList関数の引数を工夫すると,いろいろと便利にできそうです.

// 例1: Renameで始まる名前の関数のみ表示
Menu "Rename"
    FunctionList("Rename*",";","KIND:2,NPARAMS:0")
End

// 例2: 特定のウィンドウで定義されている関数のみ表示
Menu "Experiment"
    FunctionList("*",";","KIND:2,NPARAMS:0,WIN:experiment.ipf")
End

追記 (2016/11/25)

単に関数を実行する以上のことをしたい場合は,GetLastUserMenuInfo操作関数を使うとよいようです.

// ユーザー定義関数の定義部分をメニューバーからワンクリックで表示
// (Windowsだとメニュー項目の数に制限があるようです)
Menu "Definitions"
    FunctionList("*",";","KIND:2"), DisplayDefinition()
End

Function DisplayDefinition()
    GetLastUserMenuInfo
    DisplayProcedure S_Value // 直前に選択したメニュー項目がS_Valueに格納されている
End 

*1:関数の中では直接呼び出せない操作関数(SetIgorOptionとか)もあるため,常に関数のほうがよいわけではないようです

*2:「Igor Procedures」フォルダに保存しておくと起動時に読み込まれるので便利

Igor Proに標準添付されているWaveMetrics製プロシージャを使おう

はじめに

データ解析ソフトIgor Proには,実に280ものWaveMetrics社製プロシージャが標準添付されています*1

これらは#include <プロシージャ名>とプロシージャウィンドウに書くことで使えるようになるのですが,そもそもどんなプロシージャがあるのかよくわからないため,今ひとつ有効活用できていませんでした.なんともったいない!

そこで,メニューバーからワンクリックでこれらのWaveMetrics社製プロシージャを使えるようにしました.

github.com

使い方

まずは上記サイトからWMProcMenu.ipfをダウンロードしてIgorに読み込んでください.

f:id:ryotako:20161017000154p:plain

そうすると上記のようなメニューが表示され,プロシージャがアルファベット順に一覧表示されるとともに,クリックして読み込めるようになります. メニューの各部の役割は以下の通りです.

① WaveMetrics社製のプロシージャの数.

② プロシージャ名一覧.この名前をクリックするとメインプロシージャウィンドウに#include <プロシージャ名>が挿入されてコンパイルされます.コンパイル後,読み込んだプロシージャウィンドウを最前面に表示します*2

③ プロシージャを読み込んだ後に,そのプロシージャウィンドウを最前面に表示するのが鬱陶しい,という場合はこのチェックを外してください.

④ プロシージャ名一覧の取得は一度だけ行われます.Igorのファイルを別なパソコンで開き直した場合など,Igorのバージョンが変わった場合にはこれをクリックしてプロシージャ名の取得をやり直してください.

おわりに

プロシージャ名が一覧表示できるようになったことで,標準添付プロシージャが探しやすくなりました. スキャナで取り込んだグラフの画像からデータを抽出するIgorThiefなど,知らなかった便利機能がまだまだありそうです.

*1:Windows版のIgor Pro 6の場合.Mac版のIgor Pro 6だと少し数が増えて,291個のプロシージャファイルがあります.

*2:たいてい,プロシージャウィンドウの冒頭にプロシージャの使い方が書いてあります.

Igor Proで「それ全部テキストファイルで頂戴」に備える

はじめに

データ解析・グラフ作成用ソフトのIgor Pro,これ非常に便利なんですけれども,いかんせん有料ソフトなのでデータを受け渡しする際に相手がIgorを持っていないということがままあります.

そういった場合はウェーブをテキストファイルとして保存する必要があるのですが,データを一つずつ保存するのは難儀なのでプロシージャにしました.

github.com

使い方

例によって,上記のサイトからtxtsave.ipfをダウンロードしてIgor Proに読み込んでください.

そうすると以下の2つの関数が使用可能になります

  • TxtSave(データフォルダ名,ウェーブ名,ファイル名 [option])
  • TxtSave_Recursive(起点のデータフォルダ名,ウェーブ名,ファイル名 [ignore,option])

ひとつずつ説明します.

TxtSave

これはほぼ,Igor Proの操作関数Saveと同じ働きをします. 関数の引数にデータフォルダ名ウェーブ名ファイル名を指定すると,指定したデータフォルダ中にあるウェーブを,指定した名前のファイルとして保存します.

この関数と組み込みのSave操作関数の一番の違いは,ファイルの保存先のディレクトリが自動的に決定されることです.

たとえば,test.pxpというIgorのファイルで作業していた場合は,test.pxpと同じディレクトリにtest_txtfilesというフォルダが作成され,Igor Pro内部のデータフォルダ構造と同じディレクトリ構造を保ったままウェーブが保存されます*1

TxtSave("root:folder1:sub1:","dataX;dataY","data.txt")
// test.pxpと同じ階層に,test_txtfilesディレクトリが作成され,その中に`test_txtfiles/root/folder1/sub1/data.txt`が作成される.

TxtSave("","dataX;dataY","data.txt")
// 第一引数を""にした場合,現在のデータフォルダのウェーブを保存する.

TxtSave_Recursive

TxtSaveではウェーブのデータフォルダを指定して保存します.それに対してTxtSave_Recursiveでは,起点となるデータフォルダを指定すると,そのデータフォルダ以下に存在する指定した名前のウェーブを一括で保存します*2

以下の画像が,TxtSave_Recursive関数を使って,エクスペリメント中のdataXdataYという名前のウェーブを一気に保存した例です*3

便利!

f:id:ryotako:20161010145410p:plain

ignoreオプション

あるデータフォルダ以下全て保存してくれるのは便利ですが,データ解析の過程で作成したウェーブ等,無視して欲しいデータフォルダもあるかもしれません.そのような場合はTxtSave_Recursiveignoreオプションで無視するフォルダを指定します.

ignoreオプションでは*によるワイルドカード!による否定が使えます.また;を区切り文字として複数条件を指定することもできます.複数条件を指定した場合は,後に指定した条件が優先されます*4

TxtSave_Recursive("root:","dataX;dataY","data.txt",ignore="root:Packages") 
// root:Packages フォルダ以下を無視する

TxtSave_Recursive("root:","dataX;dataY","data.txt",ignore="root:Packages;*folder*") 
// root:Packagesおよび,名前にfolderを含むフォルダ以下を無視する

TxtSave_Recursive("root:","dataX;dataY","data.txt",ignore="*folder*;!folder1") 
// 名前にfolderを含むフォルダ以下を無視するが,folder1は無視しない.(後ろの条件が優先)

optionオプション

TxtSave関数は内部でSave操作関数を呼び出していますが,このときのオプションを明示的に指定することもできます.デフォルトでは,内部で

Save/G/O/W/M="\n"/B/P=[自動決定されたパス] [ウェーブ名] as [ファイル名]

としています*5.最後の/B/PTxtSave関数を使う上で絶対に必要な部分なので,ユーザーが変更できるのは/G/O/W/M="\n"の部分になります.

たとえば,空白区切りではなくタブ区切りの形式でデータを保存したい場合,以下のようにします.

TxtSave_Recursive("root:","dataX;dataY","data.txt",option="/J/O/W/M=\"\\n\"") 

メニュー

txtsave.ipfを読み込むと,メニューバーにTxtSaveという項目ができ,そこから関数を呼び出したり,保存したファイルが存在するディレクトリを開いたりできます.

また,TxtSave_で始まる関数を定義すると,このメニューから呼び出せるようになります.なので,TxtSave_Recursiveを使った関数を用意したら,TxtSave_で始まる名前にしておくと便利です.

このメニューが邪魔な場合,ファイルを読み込む際に#include "txtsave"の代わりに#include "txtsave", menus=0とするとメニューが消えます.

また,メインプロシージャウィンドウに以下のように書くとメニューが「ウェーブを保存」メニュー以下に表示されるようになります.

override strconstant TxtSave_Menu="Save Waves;-;(TxtSave"
// あるいは単に,以下のように書いてもよい
// override strconstant TxtSave_Menu="Save Waves"

おわりに

ウェーブを全てテキストファイルとして保存する,なんて,絶対に誰かがすでに方法を作っているはずで,車輪の再開発な気がしないでもないです.ただ,無視するフォルダの設定なんかは,あまり例がないんじゃないかなと思います.

*1:保存先に同名のファイルが存在した場合,デフォルトでは上書きされます.

*2:Recursive(再帰的な)の名前の通り,TxtSave関数を子フォルダに繰り返し適用するような挙動となります.

*3:データブラウザで表示してはいませんが,データフォルダfolder1にもウェーブdataXとdataYがいます.指定したウェーブが存在しないフォルダは,単に無視されます.

*4:この「後に書いた条件が前の条件を上書きする」という挙動は,gitというバージョン管理ソフトで特定ファイルを無視するときの設定(.gitignore)を参考にしています.

*5:/Mは改行文字を指定するオプションで,Windowsの場合/M="\r\n"をデフォルトで使用します