Rustのスマートポインタまとめ
最近まじめにRustを勉強し始めたので主要なスマートポインタについてRustドキュメントの内容を自分用にまとめる。
Box<T>
T
の値をヒープに格納する。型の中に同じ型が入るような無限の再帰ループを起こすような型のように、 T
のサイズが未確定のときに使える。RustドキュメントにはConsが例として挙げられている。
enum List { Cons(i32, List), Nil, } fn main() { use List::{Cons, Nil}; let ls = Cons(1, Cons(2, Cons(3, Nil))); // Consの中にConsが入っており再帰的構造になっているので、コンパイラが必要なデータのサイズを計算できない println!("{:?}", ls); }
enum List { Cons(i32, Box<List>) // Boxで定義することで再帰的構造を計算できる Nil } fn main() { use List::{Cons, Nil}; let ls = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))); println!("{:?}", ls);
Boxはポインタとして間接参照されるのでポインタがデータ構造の代わりに格納される。そのためデータの中身がどのくらいの量であるか関係なくコンパイラが解釈してくれる。
Rc<T>
Referencecounter。
複数の参照を可能にするスマートポインタ。参照されている数をカウントする機能も持っており(string_count()
, weak_count
)、このカウントが0になったとき自動で破棄されるようになっている。先程のConsを例にすると、以下のようなことができる。
#[derive(Debug)] enum List { Cons(i32, Box<List>), Nil, } fn main() { use List::{Cons, Nil}; let ls = Box::new(Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))))); println!("{:?}", ls); let ls2 = Box::new(Cons(3, ls)); // この時点で所有権がls2へ移る println!("{:?}", ls2); let ls3 = Box::new(Cons(4, ls)); // ls2へ参照権が移っているのでエラー println!("{:?}", ls3); }
use std::rc::Rc; #[derive(Debug)] enum List { Cons(i32, Rc<List>), Nil, } fn main() { use List::{Cons, Nil}; let ls = Rc::new(Cons(1, Rc::new(Cons(2, Rc::new(Cons(3, Rc::new(Nil))))))); // Rc::newで作成( カウント: 1 println!("{:?}", ls); println!("{}", Rc::strong_count(&ls)); let ls2 = Rc::new(Cons(3, Rc::clone(&ls))); // 変数lsをクローンして複製 (カウント: 2 println!("{:?}", ls2); println!("{}", Rc::strong_count(&ls)); // この時点でカウント2 let ls3 = Rc::new(Cons(4, Rc::clone(&ls))); // 変数lsをクローンして複製 (カウント: 3 println!("{:?}", ls3); println!("{}", Rc::strong_count(&ls)); // この時点でカウント3 } // ここでブロックが終わるのでカウント0、破棄 /* 出力 Cons(1, Cons(2, Cons(3, Nil))) 1 Cons(3, Cons(1, Cons(2, Cons(3, Nil)))) 2 Cons(4, Cons(1, Cons(2, Cons(3, Nil)))) 3 */
Boxはポインタだけど複数値を参照する機能は持っていないため、所有権が移った時点でRustの所有権の基本に沿ったシンプルなヒープとしての使い方が出来た。Rcを使用すれば複数参照できるヒープとして実装できる。
RefCell<T>
非常に理解に時間がかかった。
RefCellは T
の値に対して実行時に可変参照を(強制的に)許可する機能を持つ。そのため、T
が可変性を許可していなくてもRefCellの機能で可変参照を強制的に許可できる。テストなどで可変性のある値の中身を自由に入れ替えたいときなどに有効。
// 実装 trait List { fn append(&self, val: i32); } // ----------------------------------- TEST ----------------------------------- #[derive(Debug)] struct MockList { values: Vec<i32>, } impl List for MockList { fn append(&self, val: i32) { self.values.push(val); // ここが不変で借用されているのでエラー } } impl MockList { fn new() -> MockList { MockList { values: vec![] } } } // ---------------------------------------------------------------------------- fn main() { let ls = MockList::new(); ls.append(1); println!("{:?}", ls); }
// 実装 trait List { fn append(&self, val: i32); } // ----------------------------------- TEST ----------------------------------- #[derive(Debug)] struct MockList { values: RefCell<Vec<i32>>, // RefCell } impl List for MockList { fn append(&self, val: i32) { self.values.borrow_mut().push(val); // borrow_mut()で強制的に可変借用できる } } impl MockList { fn new() -> MockList { MockList { values: RefCell::new(vec![]), } } } // ---------------------------------------------------------------------------- fn main() { let ls = MockList::new(); // 作った時点では不変 ls.append(1); // appendできる println!("{:?}", ls); }
trait List
が実際の実装なのでここをテストのために可変参照を許可するようにしたくない。こうしたときに強制的に可変参照を許可できるRefCellを使うことで自由に中身を入れ替えるモックを作ることができる。
Mutex<T>・Arc<T>
Mutexはスレッドセーフな可変参照を可能にする。また、Arc<T>はMutex同様スレッドセーフに複数参照を可能にする。すなわち、MutexとArcはそれぞれRefCellとRcのスレッドセーフ版という感じ。
また、Mutexはlock()メソッドが実装されている。これを呼ぶことで排他制御を保証するためにロックの有無をLockResultとして返す。
スマートポインタはまだ種類があるみたいだけどひとまず以上。
つぎはtraitの動的ディスパッチャと静的ディスパッチャについてまとめておきたい(ちょっと勉強したけどやっぱ忘れた)
Vinpok社 Taptekのよかったところ・微妙だったところ(プログラミング編)
こちらで衝動買い www.makuake.com
2019年4月下旬購入、2019年6月19日商品到着
というような感じでまだ完全に理解した状態じゃないけどひとまず数時間触ってみた感想
よかったところ
膝の上にのせたときの安定感がよい
- ゴムが良い安定感をだしている
音がきもちよい
- パチっ、カチっというような音。個人的にすき
LEDのパターンが多い(見て飽きない)
- いちばんすきなのは押した箇所から波上にLEDが広がるように光るパターン
掃除が楽そう
- スイッチがしきつまっているので刷毛を使うだけできれいになりそう
有線と無線のハイブリッド
- 用途を使い分けれそうで大変よい
MacBookの設定が正しく反映される
F1..F12キーをデフォルトにする設定が正しく反映されてた
ちなみに以前使用していたAnkerの安いBluetoothキーボードでは反映されなかった
デバイスを3つまで登録できる
- 会社と自宅で使用するのでいちいちセッティングしなくてよいので楽ちん
微妙だったところ
右のShiftキーが小さい
- 結構多様するのですこし残念
押す力が意外といる
キーの押しごこちが、ちょっと悪い
端っこを押すと最後まで押し切られない(カチッとならない)
今までカチカチと軽快なタイピング音だったのに押しきれなくてスカッて音なる
キーの反応が早い
キーが「カチッ」となる前に反応する
組み合わせるショートカットキーの場合、いろんなキーマップが反応してしまった
あまり不快にならないオリジナル青軸、とのことだったが音の大きさに関してはもとの青軸と変わらない気がする(個人的主観)
- 音色は変わった(洗礼されたきれいな音になった)
まとめ
全体的には良い評価といえる(点数をつけるなら、70点ぐらい)
右のShiftキーが小さいことに関しては、キーマップをいじって上矢印キーをShiftキーにマッピングしたらよいのでそれで完結
押す力に関しては筋トレで完結
キーの反応と音は正直どうしようもなさそう
ひとこと
ゲームのときの使用感もレビューしたい
追記
使用して2ヶ月位でバッテリーが死んでいることに気づいた。ソッコーで友人に譲って結局自作キーボードの道へと歩んだのであった
ゴイスーなえっち本を読んだのでHaskellについてまとめた
Haskellメモ
すごいH本を読んだのでHaskellに関してメモしたことをそのまんまここに残しておく。違うところもあるかも...
基本的なこと(データ型など)については省略
すばらしい機能
パターンマッチ
- 与えられた引数に対してパターンマッチを行える
例) フィボナッチ数を求める(パターンマッチver.)
fib :: Int -> Int fib 0 = 1 fib 1 = 1 fib x = fib (x - 1) + fib (x - 2)
ガード
- 与えられた引数に対して条件式を行える
例) フィボナッチ数(ガード文ver.)
fibG :: Int -> Int fibG x | n == 0 -> 1 | n == 1 -> 1 | otherwise -> fibG (n - 1) + fibG (n - 2)
case .. of ...
- 与えられた引数に対してパターンマッチと条件式を行える
例) フィボナッチ数(case of ver.)
fibC :: Int -> Int fibC n = case n of 0 -> 1 1 -> 1 _ | n > 1 -> fibC (n - 1) + fibC (n - 2) {-- case ofは上から評価されるので本来ガード文はいらない? --}
Haskellはこのように文法は違うものの、同じ機能を持たせることができる。使い方は見やすさなどを考慮して使い分ける
特殊な式について
do
Monad値をつくるための式
let .. in
let
で関数を、in
で式を定義する式
どこにでも定義できる
例) リスト内包式
[let inc x = x + 1 in (inc 1)] {-- [2] (xを引数にとり+1して返す関数に対して1を引数に適用する式をListに内包して返す) --}
where
変数や関数を定義された式に対して束縛することができる
使い方はlet .. in
とさほど変わらないので関数の定義によって見やすさのために使い分けるべき
return
Monadを返す。CやJavaのようなプログラミング言語とは異なって結果の返り値や処理の抜け出しとしての使用法ではなく、正しく返されるべきMonadにラップされて値が返される
例) mainはIO ()
が返るべき
main :: IO () main = do let a = return "hello" {-- aの値はMonadである --} let b = "world" {-- bの値はStringである --} a >>= print {-- `>>=`の左辺が受け付ける引数はMonadのみ。print関数を呼び出したのでaはIOモナドとして関数に適用される --} print $ b {-- print関数は文字として表せるもののみ引数にとる(Monadは引数にとれない) --} print $ a {-- printはMonadを引数に取れないので構文エラーとなる --}
Functorとは?Applicativeとは?Monoid?Monad?ん?
これらをざっくりいうと、型が型を引数にもつ(型引数)もの。つまり型を持つことのできる型クラス
すごいH本がいうところの文脈付きの型
またそれらがもつ値をあらゆる形に変更・加工することのできるもの
例) Maybe型やList型([]
)はこれらすべてのインスタンスである
Maybe Int
→ Int
型を持つMaybe
という型
[Int]
→ Int
型を持つ[]
という型
このような型を引数(型引数)に新しい型を作る型(型コンストラクタ)は下に示した機能をもつことができる
前提のお話
同様に、文脈付き関数もまた直接関数を使用することが出来ない(
Maybe (a -> b)
という関数はa -> b
を直接適用することが出来ない)
Functor
文脈付き型に対して関数を適用することのできるもの
Haskellではfmap(<$>)
がFunctorに属する
例)
fmap (\x -> x + 1) (Just 1) {-- Just 2 --} (+1) <$> (Just 1) {-- Just 2 --} (+2) <$> Nothing {-- Nothing(Maybeの仕様より、値のない`Nothing`が返る) --}
Applicative
文脈を持つ値と関数同士を適用することができるもの
Haskellでは中置関数で<*>
がある
例)
(Just (+3)) <*> (Just 4) {-- Just 7 (与えられた引数に対して+3をする関数に4を与える) --} Nothing <*> (Just 4) {-- Nothing(適用される関数がないので`Nothing`) --} (Just (+3)) <*> Nothing {-- 逆も然り --}
Monoid
文脈付き型を結合することができるもの
例)
[1, 2, 3] `mappend` [4, 5, 6] {-- [1, 2, 3, 4, 5, 6] --} Just [1, 2, 3] `mappend` Just [4, 5, 6] {-- Just [1, 2, 3, 4, 5, 6] (Maybeのインスタンスである場合、中身を結合することができる) --}
Monad
文脈付き型に対して文脈付き型を返すことができるもの
これまでのFunctor、Applicative、Monoidは振る舞いが違うものの、全て文脈付き型を授かり文脈付き型を返すことに関しては全部一緒である
- つまりMonadはFunctorにもApplicativeにもMonoidにもなり得るもの??
Monadはreturn
と>>=
、>>
という関数を持つとすごいH本には書いてある
例)
return $ Just 3 {-- Just 3 (returnはClangやJavaの`return文`とは異なり、Monadをそのまま返す`関数`である) --} Just 3 >>= \x -> return $ x + 3 {-- Just 6 (`>>=`は左辺のモナドから結果だけを取り出し右辺の関数に適用した結果を新しいMonadとして返す) --} Nothing >>= \x -> return $ x + 3 {-- Nothing (MaybeはMonadなので`>>=`を適用できる) --} Just 3 >> Just 4 {-- Just 4 (両方がJustの場合、右を評価する) --} Nothing >> Just 4 {-- Nothing (言わずもがな) --}
↑たちのルール(XX則)
Functor則
- idでファンクター値を写した場合、ファンクター値が変化してはいけない
例)
fmap id (Just 3) {-- Just 3 --} fmap id [1 .. 5] {-- [1, 2, 3, 4, 5] --} fmap id Nothing {-- Nothing --}
「fとgの関数合成でファンクター値を写したもの」と「g、fと順番にファンクター値を写したもの」が等しいこと
fmap (f. g) = fmap f . fmap g
であること
Applicative則
以下の式は等価である
(pure
は純粋にその関数を返す関数)
pure f <*> x = fmap f x
pure id <*> v = v
pure (.) <*> u <*> v <*> w = u <*> (v <*> u)
pure f <*> pure x = pure (f x)
u <*> pure y = pure ($ y) <*> u
Monoid則
以下の式は等価である
mempty
mappend
x = x- 空のモノイド値に対してモノイド値を結合するとき、モノイド値が評価されること
x
mappend
mempty = x- モノイド値に対して空のモノイド値を結合するとき、モノイド値が評価されること
(x
mappend
y)mappend
z = xmappend
(ymappend
z)- 3つのモノイド値を結合するとき、それぞれの結合する順番に関係なく結果が等しくあること
Monad則
左恒等則
return
を使ってデフォルトの値をデフォルトの文脈に入れたものを>>=
を使って関数に食わせた結果は、単にその値にその関数を適用した結果と等しくあることreturn x >>= f
とf x
は等価である
右恒等則
モナディック関数
モナディック関数とは
モナド値を操作したり、モナド値を返したりする関数(両方も可!)
すごいH本より
Listは文脈付き型であるためMonadであり、Monad値を操作できる関数(map、filter...)はモナディック関数である
関数の紹介
MonadはApplicative Functorであり、Applicative FunctorはFunctorである
Monad > Applicative > Functror
liftM
ap
<*>
の強化版?<*>
-> Applicativeの関数と値同士を適用できるap -> Monadの関数と値同士を適用できる
liftA2
- 関数を2つのアプリカティブ値に適用できる
liftM2, liftM3 ...
join
Alfredとかでキーバインドを⌘+Spaceとしたときに英数も一緒に送信しちゃう
タイトルママ。
僕は普段Alfredのキーバインドを⌘+Spaceにしています。
基本的にAlfredで検索する内容は英語ばかりなので、起動したときはすぐに英数の状態になっていてほしいですね。
素早い操作に慣れてなにも確認せずに起動した瞬間にタイピングして「かな」だったときはとても苛立ちが...ピキィ!!
Karabiner-Elementsで解決
いつもどおりこういうバインディング的な作業はKarabiner-Elementsにお願いしてます。
今回は⌘+Spaceのキーバインドに合わせて英数が送信される設定に加えてCtrl+Spaceもついでに加えたルールファイルを作成しました。
{ "title": "Alfred用ルール", "rules": [ { "description": "Command + Spaceで起動時に英数キーを送信する(Alfred用)", "manipulators": [ { "type": "basic", "from": { "key_code": "spacebar", "modifiers": { "mandatory": [ "command" ] } }, "to": [ { "key_code": "spacebar", "modifiers": [ "command" ] }, { "key_code": "japanese_eisuu" } ] } ] }, { "description": "Control + Spaceで起動時に英数キーを送信する(Alfred用)", "manipulators": [ { "type": "basic", "from": { "key_code": "spacebar", "modifiers": { "mandatory": [ "control" ] } }, "to": [ { "key_code": "spacebar", "modifiers": [ "control" ] }, { "key_code": "japanese_eisuu" } ] } ] } ] }
これを~/.config/karabiner/assets/complex_modifications
配下に好きな名前でjsonファイルとして保存しておくと勝手に追加されてます。
いまさらReact + Reduxのセットアップをおさらいする
研究をろくにやらないでいるnnsです。
いろんなところでReactやReactNativeをさわる機会が多いものの覚えが悪いのでReact + ReduxのSPAプロジェクトの作成手順を毎回忘れます。
忘れないように自分なりに手順を書きます。あくまで手順メモなのでReactやReduxについての説明はほんのちょっぴりだけです。
1. Node.jsを入れる
そのまんま。バージョンはlatestを入れたほうがいいかも
2. create-react-appをグローバルでインストールする
npmを使用してReactプロジェクトをさらっと作ってくれるパッケージを入れます。
$ npm i -g create-react-app
-gオプションを加えることでグローバル環境下にパッケージを入れることができます。
3. create-react-appコマンドでReactプロジェクト作成
先ほど入れたパッケージを利用してプロジェクトを作成します。
コマンド名に続いてプロジェクト名を入力します。今回はsample-react-appにしました。
$ create-react-app sample-react-app
一応起動確認。勝手にブラウザにページが表示されると思います。
$ npm start
これでReactアプリケーションは作成されました。続いてReact-Reduxの環境を作ります。今回は最低限の環境を作るので数字のインクリメント・デクリメントをReduxで管理するアプリケーションを作成します。
4. Redux環境のインストール
作成したプロジェクトに対して新たに次のパッケージをインストールします。オプションに-S
、あるいは--save
を入れることでプロジェクト環境にパッケージをインストールすることができます。
$ npm i -S react-redux redux
これでreduxに必要な2つのパッケージがインストールされました。
5. reducerの作成
依存関係のないものから作成するのがセオリーなのでreducerから作成します。
今回、管理したいstateは表示される数字、アクションはそれに対するデクリメント、インクリメントなのでそれらの実装をsrc/reducers/index.js
配下に書いていきます。
5-1. Stateの初期状態の定義
すべてのStateは初期状態を持っている必要がありますのでこれを定義します。今回の場合、Stateである数字は0を初期値とします。
const initialState = 0
5-2. 状態を変更する関数を作成する
reducerは受け取ったアクションに対して新しい状態を返す役割を持ちます。今回の場合、デクリメント、あるいはインクリメントというアクションに対して状態である数字が変わるので、numという関数にします。
ちなみに、データフローにしたがって、初期起動時に必ずreducerは通るのでswitch文にdefaultを入れるのを忘れずにします。
function num(state = initialState, action) { switch(action.type) { case 'DECREMENT': { return state - 1; } case 'INCREMENT': { return state + 1; } default: { return state; } } } export default num;
6. Actionの作成
actionの役割はcomponentとreducerの橋渡し的なものです。component側から呼び出されたactionのタイミングに合わせてreducerが状態を変更してくれるという流れです。
実際のコードです。src/actions/index.js
配下に書いていきます。
// action types const DECREMENT = 'DECREMENT'; const INCREMENT = 'INCREMENT'; // action creators function decrement() { return { type: DECREMENT, }; } fucntion increment() { return { type: INCREMENT, }; }
actionにはreducer側でどのアクションが呼び出されたかを識別するためのActionTypeとcomponent側で呼び出すために使用するActionCreatorに分けられます。
変更されるStateに対して追加の情報を与えたいときはActionCreatorの関数に対して引数で渡します。そしてreturn文の中のオブジェクトに新しくその値をプロパティと添えて入れてあげるだけです。
7. Componentの作成
React-ReduxのComponentには大きく分けてContainer ComponentとPresentational Componentの2つがあります。
Container Componentから先に作ります。
7-1. Container Componentの作成
Container Componentはデータやコールバック関数などといったビジネスロジックをもったコンポーネントです。ですので実際の描画とはあまり関わることがほとんどありません。Stateを各Componentが持つPropsに変換するのもここで行います。
/src/container/AppContainer.js
に記述。/App.js
を一部書き換えます。
import React from 'react'; import { connect } from 'react-redux'; import App from '../component/App' import logo from '../logo.svg'; import '../App.css'; class AppContainer extends React.Component { constructor(props) { super(props); this.decrement = this.decrement.bind(this); this.increment = this.increment.bind(this); } decrement() { alert('デクリメント'); } increment() { alert('インクリメント'); } render() { const { num } = this.props; return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> <p className="App-intro"> To get started, edit <code>src/App.js</code> and save to reload. </p> <div> <App num={num} decrement={this.decrement} increment={this.increment} /> </div> </div> ); } } function mapStateToProps(state) { return { num: state, }; } export default connect(mapStateToProps)(AppContainer);
decrement()
とincrement()
は状態を変更するためのコールバック関数です。propsとしてComponentに渡すことでComponentのイベントリスナーに応じて反応します。
mapStateToProps()
は関数名の通り、reduer(store)で管理しているStateをComponentで扱えるようにpropsとして変換しています。connect()
はそのヒモ付を行ってくれる関数です。このファイルを呼び出すときにconnectされたものをデフォルトとしてインポートしてほしいのでconnectをexport default
しています。
7-2. Presentational Componentの作成
Presentational Componentは描画のみを行うComponentです。ボタンを押したときの処理は後に記述するContainer Componentが担当するので、Presentational Componentはpropsから預かったコールバック関数(decrement()
、increment()
)を呼び出すだけです。
src/component/App.js
に記述します。
import React from 'react'; class App extends React.Component { render() { const { num, decrement, increment } = this.props; return ( <div> <button onClick={() => decrement()}> - </button> <button onClick={() => increment()}> + </button> <div>{num}です</div> </div> ); } }
8. StoreをContainer Componentに注入
最後に、状態を持つreducer(Store)をContainer ComponentであるAppContainerに注入します。
/index.js
を以下のように変更
import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; import './index.css'; import App from './App'; import AppContainer from './container/AppContainer' import registerServiceWorker from './registerServiceWorker'; import reducers from './reducers/' const store = createStore(reducers); ReactDOM.render( <Provider store={store}> <AppContainer /> </Provider>, document.getElementById('root'), ); registerServiceWorker();
createStore()
の引数にreducerを代入することでstoreとして作成することができます。そしてContainer ComponentをProvider
でラップ、storeというpropsにcreateStore()
で作成したstoreを指定あげれば準備OK
9. Stateを変更できるかの確認
ここまでで一度起動すると、ボタン2コと「0です」というテキストが新たに作成された画面ができます。ボタンを押すとアラートが出るのみです。
ここでContainer Componentのコールバック関数内の内容を書き換えて実際にStateを変更します。
/src/container/AppContainer.js
を以下に変更
import { decrement, increment } from '../actions/' .... decrement() { const { dispatch } = this.props; dispatch(decrement()); } increment() { const { dispatch } = this.props; dispatch(increment()); } ....
Providerでラップされたコンポーネントにはdispatch()
という関数が備わっています。これにActionCreatorを引数に与えてあげることでアクションがアタッチされ情報がreducerに送られます。
もう一度起動。無事インクリメント、デクリメントができました。
個人的にはこれに対してTypeScriptを導入したりすると、型の恩恵によりストレスフリーな開発ができるのでおすすめです。おしまい
Karabiner-Elementのプラグイン「Vim-emulation」でVivaldiを無視するルールを追加する
前フリ
前回の続きのお話です。
もっといろんなアプリをVim化したいという願望をもっていた僕でしたが、結局KaraBiner-Elementの「Vim-emulation」というプラグインを入れることで落ち着きました。
いろんなアプリをVim化する上でESCキーがアプリと競合して「今どっちのESCキーが反応したの?」とわちゃわちゃするのがとっても嫌だったのですが、Vim-emulationを使用する場合、「ctrl + [」キーのみでインサートモードを解除することで解決しました。(まだ慣れていませんが)
本題
SurfingKeysと競合しあう問題はVim-elementsの設定ファイルをいじることで一部のアプリに対して適用を無視することができます。
Chrome、iTermなど基本的なアプリは標準の設定で無視されています。ありがたい。
僕が普段使っているVivaldiは設定されてないので、自分で入力する必要があります。
しかしながらこの「Vim-emulation」、設定ファイルの行数が何十万行にも渡る巨大なファイルになっているのでこれを見て手入力するのはとても疲れます。(途中までやってしんどかた)
絶対にもっとイケてる方法はあるんやろなあとか思いながら自分でスクリプト書きました。 Python初心者なのでコードの汚さはご愛嬌
import json add_name_arr = [ "^com\\.vivaldi\\.Vivaldi$", ] json_data = {} with open("~/.config/karabiner/assets/complex_modifications/{設定ファイル.json}", "r") as res: json_data = json.load(res) with open("~/.config/karabiner/assets/complex_modifications/{設定ファイル.json}" "w") as res: for i in range(len(json_data['rules'])): manipulators_ary = json_data['rules'][i]['manipulators'] for j in range(len(manipulators_ary)): manipulators_obj = manipulators_ary[j] # "conditions"キー、"type"、"bundle_identifiers"を追加 if 'conditions' not in manipulators_obj: print("conditions not exists") manipulators_obj.update({"conditions": [{"type": "frontmost_application_unless", "bundle_identifiers": []}]}) bundle_identifiers = manipulators_obj['conditions'][0]["bundle_identifiers"] # 追加しようとしている要素がなければ"bundle_identifiers"に追加 for add_name in add_name_arr: # ここをいじればキーを削除するスクリプトに変えれる if (add_name not in bundle_identifiers): bundle_identifiers.append(add_name) print(json.dumps(json_data, indent = 2)) # エンコード(書き込みモードなのでそのまま上書き) json.dump(json_data, res, indent = 2)
エラーなくvivaldiが追加されたjsonが出力されたら成功です。
add_name
内のアドレスを変更することで他のアプリもルールに追加することができます。
設定ファイルはすべてのプラグインにおいて同じフォーマットなので、もしかしたら「Vim-emulation」に限らずとも使用できるかもしれないです。(試してない)
コードの全体は僕のdotfilesにあります
追記
jsonは正しく出力されると思いますが追加したプラグインが既に有効化されている場合、設定が正しく反映されないのでComplex Modifications
タブから対象のプラグインをRemoveした上で再度有効化してください。