正規表現の最短マッチングにおける"?"についての瑣末な話
最短マッチ
私は正規表現について簡単な用途では使える気でいたのですが,あるとき次のような正規表現に出会いました.
.*?
.*
は任意の文字が0個以上,というのはわかるのですが,そのあとの?
がわからない.
もちろんこの正体は,ご存知の方は多いでしょうが「最短マッチ」というものです.
私はこれに出会うのは初めてだったので,なるほど…と思ったのですが,これを見た時に色々考えてしまいました.
「?は0個か1個かを表す意味のメタ文字であるはず」
「ということは .*
が0個か1個かを探しているのか?」
「いや,でもどう考えてもそれじゃ最短マッチにならない…」
結局考えると「?
によって最短マッチとなるのは?
の機能を活用したものなのか,それとも単に?
に最短マッチとなる効果があるのか」という疑問にまとまり調査した結果,後者らしいということがわかりました.
Regex Tutorial - Repetition with Star and Plus曰く,「repititionオペレータはgreedyであるが,?
をつけるとlazyにできる」とあったのでこれで結論みたいです.
他のサイトでもメタ文字一覧の?
にも同じことが書かれていたりしました.
おまけ
?
という0個か1個というrepititionにも?
が使えるらしいので試して見ました.
文字列"ab"に対して
ab?
->ab
にマッチab??
->a
にのみマッチ
ちゃんと動作しているようです.使い所あるのか…?
iPhoneXはAppleのマルチタスキングに対する回答かもしれない
iPhoneX
発売してから半年以上が経過したiPhoneXですが,私も手に入れてから半年近く愛用しています.
そもそもこの機種にしたのは画面が大きい方が電子書籍とか動画とか見やすいだろうという理由でした. 電車で大学に通学する途中でもちろんkindleなどを開いて電子書籍を読むのですが,実際読みやすいです. 前の機種がiPhone5Sというのもあり,画面サイズの大型化は思った以上に良かったです.
そんな中で,ふとiPhoneXを使った効率の良い電子書籍の読み方に気が付いたので,ここに書いてみようと思います.
TL;DR
メモとkindle(などのリーダ)を高速に行き来できるため,本を読みながらのメモが非常に捗る.
(以降は若干ポエム気味です)
iPhoneXにしかない機能
iPhoneXにしかない機能といえば何でしょうか. FaceID?デュアルカメラ?
Appleは(私の知る限り)新商品には必ず新しいインタラクションを備えています. iPhoneXで変わったことといえば,やはりホームボタンの消失が一番大きかったと思っています. ベゼルレスを実現するのに必要だったとはいえ,これまで操作が大きく変わるきっかけになりました.
ホームボタンを押す代わりに,画面の下端からスワイプする. アプリを切り替えるときには,画面の下端を横にスワイプする.
新しいインタクションでしたが,実際に使うとすぐに慣れました.
画面の下端をスワイプするだけで,kindleとメモを行き来できる
この「アプリを切り替えるときには,画面の下端を横にスワイプする」という操作ですが,これが画期的な操作感を生み出していると考えています. これまでの機種(Android含め)はアプリ間の切り替えにホームボタンのダブルタップやアプリ切り替えボタンのタップが求められており,一つの操作では切り替えられませんでした(Galaxyのように2画面同時表示などで解決した例もありますが…iPadも指4本スワイプで切り替えることはできます). それがこのiPhoneXでは,「親指1本で横にスワイプする」だけでアプリの切り替えを行うことができます.
本を読みながらメモをとるというのはとても有効な読み方ですが,電車の中で行うには紙の本では難しく,タブレットはサイズが大きい,重いといったデメリットがありました. このインタラクションを用いることで,iPhoneXでは「メモとkindleをそれぞれ開き,横スワイプで切り替えながら,メモを取りつつ読む」が可能になります. 実際この手法を採用してからは本を読む効率が格段に上がりました. 電車の中だけでなく,右手が空いていれば本をメモ取りながら読めるので.
マルチタスキングのUI
Galaxyの2画面同時表示やiPadの指4本スワイプなど,様々なモバイルデバイスによるマルチタスキング*1インターフェースが考案されてきましたが,私はiPhoneXのインターフェースが一番使いやすいのではないかと考えています. 画面が小さいので,2つのアプリを同時に表示するのはとても見にくくなります. この画面の小ささという制約の中で,簡易な操作によるマルチタスクを実現したインタフェース. 正直iPadにも欲しいです.
狭い画面の中で複数のアプリを同時使用するのに必要なインターフェースという問いに対するAppleの答えが,iPhoneXによるベゼルレスを生かした横スワイプ切り替えなのではないかと思いました.
PlatformIOでArduinoの純正IDEから卒業する
PlatformIO
久々にArduinoを使うことになり,せっかくだしコマンドラインから開発したいなぁとなって調べているうちに,良いものを見つけました.
VSCodeの拡張機能としてインストールでき,ArduinoのIDEが無くても動作する優れモノです. 今回はPlatformIOの導入方法について説明してみたいと思います.
Arduinoの純正IDE
(もしかしたら私が知らないだけの機能があるかもしれないです) よくプログラミングをする人はわかると思いますが,Arduinoの純正IDEは機能がとても少ない… 複数のファイル編集もタブしかなく貧弱ですし,変数や関数の補完もしてくれない. Vimのキーバインドも使えないし…
いわゆるVSCodeみたいなインテリジェントなエディタに対して機能が貧弱なんですね.
PlatformIO
そこで開発されているのがPlatformIOで,コマンドラインから使えるマイコン向けのビルド・書き込みツールです. Arduinoだけでなく色々なマイコンに対応しているそう.
コマンドラインから使える,と言うことはコーディングと書き込みで別のツールとして使うこともできます. スクリプトを書いて自動で書き込みとかもできる. しかし,ボードの正式名称を調べたり決められたコマンドを使わなければいけなかったりと設定ファイルを作るのがちょっと面倒だったりする.
コマンドを覚えるのが面倒,そう言う人のために,PlatformIOを用いたIDE,PlatformIO IDEと言うものが用意されています.
また新しいIDEを入れなくてはいけないのか…と思いきや,ページを開けばわかりますが,AtomだったりVisualStudioCodeの拡張機能として導入できます(と言うより拡張機能がインストールされたAtomがダウンロードできるといった様子です).
VisualStudioCodeの拡張機能として導入するメリットは以下の通り.
- 普段使い慣れたエディタで開発できる
- GUIベースでプロジェクトの設定が可能
- ボードの選択をはじめとしたプロジェクトの設定をビジュアルに行うことができる
- ビルドや書き込みもワンボタン
- PlatformIOの拡張機能をインストールすると左下に専用のボタンが現れ,ビルドや書き込みをワンボタンで行うことができる
VisualStudioCodeにPlatformIOを導入する
VisualStudioCodeは軽量かつ高機能なエディタとして誰にでもお勧めできます. インストールはこちらから.
何となくやればインストールは終わるはず.
次に拡張機能をインストールします.
左側のアイコンのうち一番下をクリックし,検索窓にplatform
まで入れれば一番上に出てきます(画像ではすでにインストールされていますがインストールボタンが出てくるはず).
基本それだけ.
インストールできるとPlatformIO Homeといったウィンドウが出てくるはず…または左下の家のマークをしたボタンからも開けます.
開いた時の画像.
NewProjectと言うボタンがあるはずなので,それをクリックしボードなどを選びます. ボード選択の部分は文字を入力でき絞り込みが行えます. フォルダの配置場所はご自由に.
プロジェクトが作成されると,フォルダ階層が表示されるはず.
プログラム本体はsrc/main.cpp
にあるので,これを開き,Lチカのコードを書いてみます.
あとは左下にある「→」ボタンをクリックすると,ビルドと書き込みが行われます.
その他の機能
Arduinoを使う上でシリアルモニタの使いやすさは捨てがたい. もちろんPlatformIOにもその機能が搭載されています. 左下にあるアイコンのうち右のほうにある,コンセントみたいなアイコンをクリックするとシリアルモニタがターミナル上に表示されます. ちゃんと動く.便利.
まとめ
以上,VisualStudioCodeでPlatformIOを導入するメリットとその方法について説明してきました. 使い慣れたIDEで開発できる,しかもGUIでプロジェクトの設定やビルド・書き込みができるところが非常にありがたい. さらにシリアルモニタまで使えるとか,純正のIDEをはるかに超えた使いやすさでした.
注意
以前platformioを使おうとした時に,PythonのVer2を使えるようにしなくてはならず,PyenvでVer3を使っていた自分にとっては使いづらかった記憶があります(今は消しましたが…). 同様にPyenvを使っていてVer3をデフォルトにしている人は何かしら上記の手順でエラーが出るかもしれません. 今どのバージョンに対応しているのかがぱっと見わからなかったので,もしかしたらすでにVer3に対応しているのかもしれません.
2値画像中のある円領域内の画素数をカウントしたい
コーディング中…
既存論文の実装のために,2値画像中のある円領域内の画素数をカウントする必要が出てきた. 円領域…ということで愚直に実装してみたところ
# imgが画像(numpy array) # p_x, p_yが円を囲む正方形領域の左上の点 # lengthが円領域の直径 c_x = length / 2 + p_x c_y = length / 2 + p_y radius = length / 2 count = 0 for x, y in itertools.product(range(p_x, p_x+length), range(p_y, p_y+length)): if ((x - c_x)**2 + (y - c_y)**2 <= radius**2) and img[x][y] > 0: count += 1
… 終わらない… 5分経っても終わらない…
調べてみた
こうなることはある程度予測できていたので調べてみた. すると次のStackOverflowのページがヒットした.
np.ogrid
というものを使っているらしいが時間がないのであとで調べようということで,次のように実装し直した.
createCircularMask
はTrue
とFalse
によるマスクを返してくれるので,正方形領域であるwindow
を抽出したあとでそのマスクをかける.
あとはただのnp.array
なのでnp.count_nonzero
を実行すればカウントできる.
def createCircularMask(h, w, center=None, radius=None): if center is None: # use the middle of the image center = [int(w/2), int(h/2)] if radius is None: # use the smallest distance between the center and image walls radius = min(center[0], center[1], w-center[0], h-center[1]) Y, X = np.ogrid[:h, :w] dist_from_center = np.sqrt((X - center[0])**2 + (Y-center[1])**2) mask = dist_from_center <= radius return mask c_x = length / 2 c_y = length / 2 radius = length / 2 count = 0 window = img[p_x: p_x+length, p_y: p_y+length] window = window[createCircularMask(length, length)] count = np.count_nonzero(window)
すぐに結果が返ってきた. やはりnumpyは偉大ですね.