ひとりはみんなのせいで

調べたことの備忘録など

smartparensコマンド

括弧編集スキルを上げるため、M-x sp-cheat-sheetに出てくる編集コマンドを上から少しづつ覚えることにした。

sp-forward-sexp

S式を一つ前に進める。デフォルトのバインドはC-M-f。 括弧であれば対応する括弧に移動する。

f:id:tei6:20170808230606g:plain

文字列であれば空白まで。

f:id:tei6:20170808230617g:plain

universal argumentで実行回数を指定できる。C-2ならこう。

f:id:tei6:20170808230658g:plain

閉じ括弧に向かう場合、閉じ括弧の次の位置まで移動する

f:id:tei6:20170808230711g:plain

sp-backward-sexp

S式を一つ後ろに進める。sp-forward-sexpと逆の操作。デフォルトのバインドはC-M-b。 対応する括弧への移動。

f:id:tei6:20170808231159g:plain

文字列の移動。

f:id:tei6:20170808231210g:plain

universal argument。

f:id:tei6:20170808231219g:plain

開き括弧に向かう場合。

f:id:tei6:20170808231233g:plain

Spacemacsでcider-jack-inした際にREPLウィンドウが開かない問題

Spacemacsを導入した時、cider-jack-inでウィンドウ分割とREPLバッファ への切り替えが起きなくなった(裏でnreplへは接続される)。不具合かなーと思いつつ、 毎回REPLウィンドウを手動で開いていたが、原因を調べてみることにした。

答えは単純で、Spacemacsの初期化時にそう設定していたからだった。 spacemacs/layers/+lang/clojure/packages.elの関数clojure/init-ciderで cider-repl-pop-to-buffer-on-connectをnilに設定しているので、.spacemacs中に以下を 追加すれば元の動作に戻る。

(defun dotspacemacs/user-config ()
  (setq cider-repl-pop-to-buffer-on-connect t))

ただ、デフォルト設定がウィンドウ分割OFFということは、そういう使いかたの方が多数 派なのかなーと思ったり。Schemeを使っていた頃から、REPLウィンドウが開くのが当たり 前だと思っていたけど、ミニバッファの表示だけでも結構なんとかなるのかもしれない。 しばらくデフォルトの動作のまま使ってみることにした。

ちなみにcider-jack-inした後ならSPC m s sでウィンドウ分割とREPLバッファの切り替え ができるようです。

Excel仕様書の段落番号を自動採番する

Excelで仕様書を書くのは嫌な作業だけれど、時に段落番号の手打ちが苦痛だったので、自動 採番する数式を考えた。段落番号のフォーマットは、各階層の番号をデリミタでつな ぐものとした。(例: 1-1-1.)

=MID(INDIRECT("$B" & MAX(IF($B$3:$B7<>"",ROW($B$3:$B7)))),1,LEN(INDIRECT("$B"&MAX(IF($B$3:$B7<>"",ROW($B$3:$B7)))))-1) & "-" & COUNTA(INDIRECT(ADDRESS((MAX(IF($B$3:B7<>"",(ROW($B$3:$B7))))+1),COLUMN()) & ":" & ADDRESS((ROW()-1),COLUMN())))+1 & "."

配列数式なので、貼った後にCtrl+Shift+Enterする。

動作

一つ上の段落から、最後の要素を探して、同じ段落中の自分の連番を末尾に追加する。 こんなイメージ。

f:id:tei6:20170724234627p:plain

以下の式で上の段落の最後の要素を参照する。

INDIRECT("$B" & MAX(IF($B$3:$B7<>"",ROW($B$3:$B7))))[f:id:tei6:20170724234627p:plain]

また、

COUNTA(INDIRECT(ADDRESS((MAX(IF($B$3:B7<>"",(ROW($B$3:$B7))))+1),COLUMN()) & ":" & ADDRESS((ROW()-1),COLUMN())))

で自セルの段落中の通番をカウントする。

EmacsのS式編集

EmacsのS式編集に関する覚え書き(標準機能+Smartparens)。

準備

Smartparensを導入する。もしSpacemacsを使って いれば最初から入っている。そうでなければmelpaからpackage-installできる。

M-x package-install RET smartparens

.emacsに以下を追加。2行目 は作者と同じ設定に するためのもの。

(require 'smartparens-config)
(sp-use-smartparens-bindings)

基本コマンド

よく使うコマンド。これくらい覚えておけば、そこそこ快適に編集ができるはず。

移動

次の/前のS式に移動(C-M-f/C-M-b)

S式単位で移動する。

f:id:tei6:20170711235033g:plain

S式を上る/下る(C-M-u/C-M-d/C-M-e/C-M-a)

一つ外側、または内側のS式に移動する。C-M-u/C-M-dで前方の括弧を上り/下り、C-M-e/C-M-aで後方の括弧を上り/下りする。

f:id:tei6:20170711235611g:plain

選択

S式を選択(C-M-SPC)

カーソル以降のS式を選択する。複数回実行でリージョンが手前に広がる。

f:id:tei6:20170712000030g:plain

編集

対応する括弧の自動挿入

左括弧を入力すると、対応する括弧が自動で挿入される。

f:id:tei6:20170712000446g:plain

リージョン選択中の場合、リージョンを囲むように括弧が挿入される。

f:id:tei6:20170712000857g:plain

括弧を移動(C-right/C-left/C-M-right/C-M-left)

1つ外側の括弧の位置を移動する。C-rightで右側の確固を右方向に移動。C-leftで右側の 確固を左側に移動する。C-M-right/C-M-leftで左括弧について同様の操作ができる。

f:id:tei6:20170712001721g:plain

S式のインデント(C-M-q)

S式をインデントする。

f:id:tei6:20170712002125g:plain

S式のレベルを下げる(C-M-backspace)

外側の括弧を消して、S式のレベルを1段下げる。カーソルより左側にあるS式は消える。

f:id:tei6:20170712002353g:plain

Dijkstra's algorithm

ダイクストラ法についてしっかり勉強したことがなかったので実装してみた。特に新規性はないけど折角書いたので記録しておきます。 グラフはこちらのブログを参考にした。

(def m {:a {:b 5, :c 4, :d 2}
        :b {:a 5, :c 2, :e 6}
        :c {:a 4, :b 2, :c 3, :f 2}
        :d {:a 2, :c 3, :f 6}
        :e {:b 6, :f 4}
        :f {:c 2, :d 6, :e 4}})

(defn dijkstra [from-node to-node m]
  (loop [cur-node from-node ;; 対象ノード
         unfixed-nodes (-> m keys set (disj cur-node)) ;; 未確定ノード
         routes {cur-node {:c 0, :r [cur-node]}}] ;; コストと経路
    (if (empty? unfixed-nodes)
      ;; 未確定ノードがなければ探索終了
      ;; 目標ノードまでのコストと経路を返却
      (routes to-node)
      (let [;; コストと経路を更新
            routes (let [r (routes cur-node)]
                     (->> (m cur-node)
                          (filter #(unfixed-nodes (key %)))
                          ;; 対象ノードと繋がっている各ノードについて
                          ;; 対象ノードから辿った場合のコストと経路を計算
                          (into {} (map (fn [[k v]] [k {:c (+ (:c r) v) 
                                                        :r (conj (:r r) k)}])))
                          ;; これまでより小コストであれば更新
                          (merge-with #(min-key :c % %2) routes))) 
            ;; 確定ノードを検索
            ;; 開始ノードからのコストが最も小さいノードを選定
            [fixed-node _] (->> (for [[k _ :as e] routes :when (unfixed-nodes k)] e) 
                                (apply min-key #(-> % second :c)))]
        (recur fixed-node (disj unfixed-nodes fixed-node) routes)))))

(comment
  (dijkstra :d :e m) ;; => {:c 9, :r [:d :c :f :e]}
  (dijkstra :c :e m) ;; => {:c 6, :r [:c :f :e]}
  )

Excelと同様の形式で数値文字列を取得する

VBAで数値を文字列として扱いたい場合、特に変換処理は必要なく

Cells(1,1).Value

で取得できる。文字列との連結等もうまくやってくれる。 .Valueプロパティにおける文字列変換仕様は概ねJavaのDouble.toStringと同じだけれど、微妙に異なる点もある。たとえば

Double.toString(1.0) // ⇒ "1.0"

に対して

Cells(1.1).Value ' ⇒ "1"

など。

Java側でExcelの仕様通りに数値文字列を取得したい場合が(稀に)ある。 たとえば現行システムの再構築の際にVBAで組まれた帳票取り込みをJavaで再現しないといけないとか。 そういうときのためのクラスがあり、

NumberToTextConverter.toText(1.0D) // ⇒ "1"

で取得できる。すごい。細かな変換仕様はリンク先を参照

  • 参考文献

NumberToTextConverter (POI API Documentation)

Apache POIで選択範囲を走査する

Excelで、ある範囲内に対して同じ処理をしたい場合がある。VBAだとこんな感じ

Sub IncrementValues()
    For Each r In Range("A1:D4")
        r.Value = r.Value + 1
    Next r
End Sub

これをApache POIでやろうとすると取得したCellオブジェクトがnull場合の考慮など結構面倒なのだけど、リファレンスを見ていたらちょっと便利なユーティリティクラスCellWalkを発見した。

FileInputStream fis = new FileInputStream(workbookPath);
Workbook wb = WorkbookFactory.create(fis);
Sheet ws = wb.getSheetAt(0);

CellRangeAddress cra = new CellRangeAddress(0, 3, 0, 3); // "A1:D4"
CellWalk cw = new CellWalk(ws, cra);
cw.traverse(new CellHandler() {
    @Override
    public void onCell(Cell cell, CellWalkContext cwc) {
        cell.setCellValue(cell.getNumericCellValue() + 1);
    }
});
End Sub
  • CellHandlerで各セルへの操作を定義する
  • CellWalkContext#getRowNumber()で行インデックス、CellWalkContext#getColumnNumber()で列インデックスを取得
  • 空白のセルには処理が行われないので、そこは注意が必要