Julia - 野良パッケージをパッケージ名だけで追加できるようにする: Custom General Repository の作り方

Julia でパッケージを追加する場合、pkg mode で

(v1.0) pkg> add Example

などとすれば良いですよね、公式パッケージ*1の場合は。

野良パッケージの場合は次の例のように github の URL を打ち込まないといけません。

(v1.0) pkg> add https://github.com/goropikari/MyPackage.jl

パッケージ名だけならともかく URL を打ち込まないといけないというのは面倒です。公式パッケージ同様パッケージ名だけで追加したいと思うのは当然の願いでしょう。

この記事では Custom General Repository を作り、公式パッケージ同様 野良パッケージもパッケージ名だけで追加できるようにする方法を紹介します。

f:id:goropikarikun:20180922212714p:plain 上手くいくとこんな感じで野良パッケージがパッケージ名だけで追加することができるようになります。

目次

環境

  • OS: ArchLinux
    OS 依存する内容ではありませんが、Linux コマンドを使っているので適宜脳内変換してください。
  • Julia 1.0

Custom General Repository

Custom General Repository とは?

Custom General Repository とは何か?と普通の人なら思うでしょう。無理もありません、便宜上私がそう呼んでいるだけですから。

Julia 0.x 時代のパッケージマネージャーはパッケージの情報を METADATA から参照していましたが、Julia 1.0 からは METADATA に変わり General を参照するようになりました。

それで、これから我々がやろうとしていることを Julia 0.x 時代は Custom METADATA Repository と呼称していたので Custom General Repository としただけです。

github.com

github.com

Custom General Repository を作るメリット

現状の Julia では、パッケージ名で追加できるパッケージ公式パッケージ は同値です。

しかし、以前の記事にも書きましたが、公式パッケージにするためには README を英語で書いて、やりとりも英語でしなければならないので、非ネイティブな我々日本人にとってはハードルが高いものになっていることも事実でしょう。 おまけにレビュー付きなので「ちょっと練習がてら空のパッケージ登録をしてみよ」なんてことも出来ません。

goropikari.hatenablog.com

また、ローカルのコミュニティでしか使わないパッケージを運用したい場合や、" 田中コレクション " といった独自のパッケージ群を提供したい場合なども公式パッケージに登録することは適しているとは言えないでしょう。

そんな時に便利なのが Custom General Repository です。これを作ることによって自分たちの裁量によって野良パッケージを自分の General に登録することができ、公式パッケージ同様にパッケージ名だけでパッケージを追加することができるようになります。

Youtuber の 「チャンネル登録はこちら」ではありませんが、「私のパッケージ群を使いたい場合はこの URL を git clone♪」とするだけで他の人たちは気軽にあなたのパッケージを使えるようになります。

Custom General Repository を作る

以下の内容は私が実験しながら模索した方法であり、公式のやり方ではありません。そもそも、公式ドキュメントに独自の General を作るやり方は 2018/9/22 現在書いてありません*2

先にどんなものなのか試したい人は MyGeneral を配置する を読んでください。

まずはご自身の github にパッケージを管理するためのリポジトリを作ってください。ローカルネットワーク内のディレクトリなどでも良いのですが、わかりやすさのため今回は github で作ります。リポジトリの名前は何でもよいですが、ここでは MyGeneral としました。

以下はこの MyGeneral に私の野良パッケージ GameOfLife.jlMyPackage.jl を登録するまでの流れです。先頭が $ から始まるものは bash での操作を表します。

作ったリポジトリをクローンしてきて必要なファイルを作っておきます。

$ git clone https://github.com/goropikari/MyGeneral.git
$ cd MyGenaral
$ touch Registry.toml

$ mkdir GameOfLife
$ cd GameOfLife
$ touch Compat.toml Deps.toml Versions.toml Package.toml

Registry.toml の中身はこんな感じ。

name = "MyGeneral"
uuid = "0d136408-be1b-11e8-146b-1b5bc37d4128"
repo = "https://github.com/goropikari/MyGeneral.git"

[packages]
b53c8852-a93e-11e8-3341-5bdef8841476 = { name = "GameOfLife", path = "./GameOfLife" }

name を作ったリポジトリMyGeneral にする。

uuid は適当に generate MyGeneral して出てきたものを使いました。

(v1.0) pkg> generate MyGeneral
Generating project MyGeneral:
    MyGeneral/Project.toml
    MyGeneral/src/MyGeneral.jl

shell> cat MyGeneral/Project.toml
authors = ["goropikari <goropikari56@gmail.com>"]
name = "MyGeneral"
uuid = "0d136408-be1b-11e8-146b-1b5bc37d4128"
version = "0.1.0"

[deps]

repo にはリポジトリがある URL を入れてください。

続く [packages] の項目ですが、初めの謎の文字列は GameOfLife.jlProject.toml に記載されている uuid です。path には GameOfLife.jl の情報が書かれているファイル*3がある場所を指します。今は MyGeneral の直下に置いたので ./GameOfLife となっています。

現在のディレクトリ構成

MyGeneral/
├── GameOfLife
│   ├── Compat.toml
│   ├── Deps.toml
│   ├── Package.toml
│   └── Versions.toml
└── Registry.toml

続いて Compat.toml, Deps.toml, Versions.toml, Package.toml に必要な情報を書いていきます。

Deps.toml

GameOfLife.jlProject.toml[deps] に書いてあったものをそのまま書きます。 最初の ["0.1"]GameOfLife.jl のバージョン 0.1.0 の意味です。 要するに GameOfLife.jlのバージョン x.x.x はこれらのパッケージに依存してますよ〜と書かれているのが Deps.tomlです。

["0.1"]
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"

Versions.toml

GameOfLife.jl の tree オブジェクト の SHA-1ハッシュ を書きます。

["0.1.0"]
git-tree-sha1 = "0ea24bdd852ad9505eab96815e6f2c4dadffc018"

ハッシュ値は GameOfLife.jl パッケージ本体*4ディレクトリ上で

$ git cat-file -p HEAD
tree 0ea24bdd852ad9505eab96815e6f2c4dadffc018
parent 2f5c85c856f67d2db3152801507f30b640a83849
author goropikari <goropikari56@gmail.com> 1535299189 +0900
committer goropikari <goropikari56@gmail.com> 1535299189 +0900

とすると調べることができます。

Compat.toml

対応している Julia のバージョンなどを書きます。Pkg.jl のドキュメント に書いてある方法に従えば良いと思いますが、当面は以下のように書いておけばよいでしょう。

["0.1"]
julia = "0.7-1.1"

Package.toml

パッケージの情報を書きます。uuid は Project.toml に書いてあるものを書きます。 repo にはパッケージが置いてある github URL を書いておきます。

name = "GameOfLife"
uuid = "b53c8852-a93e-11e8-3341-5bdef8841476"
repo = "https://github.com/goropikari/GameOfLife.jl.git"

push する

MyPackage.jl の方も同様にして追加し、github に push します。

$ git add -A
$ git commit -m 'first commit'
$ git push

MyGeneral を配置する

作った MyGeneral~/.julia/registries に配置します。

$ julia -e 'using Pkg; Pkg.update()'
$ cd ~/.julia/registries
$ git clone https://github.com/goropikari/MyGeneral.git

Julia を起動し、パッケージ情報をアップデートするといつもの https://github.com/JuliaRegistries/General.git に加えて https://github.com/goropikari/MyGeneral.gitも読みにいっていることがわかります。

(v1.0) pkg> up
  Updating registry at `~/.julia/registries/General`
  Updating git-repo `https://github.com/JuliaRegistries/General.git`
  Updating registry at `~/.julia/registries/MyGeneral`
  Updating git-repo `https://github.com/goropikari/MyGeneral.git`
 Resolving package versions...

(v1.0) pkg> add GameOfLife
 Resolving package versions...
 Installed GameOfLife ─ v0.1.0
  Updating `~/.julia/environments/v1.0/Project.toml`
  [b53c8852] + GameOfLife v0.1.0
  Updating `~/.julia/environments/v1.0/Manifest.toml`
  [b53c8852] + GameOfLife v0.1.0
  [2a0f44e3] + Base64 
  [b77e0a4c] + InteractiveUtils 
  [8f399da3] + Libdl 
  [37e2e46d] + LinearAlgebra 
  [d6f4376e] + Markdown 
  [3fa0cd96] + REPL 
  [6462fe0b] + Sockets 

(v1.0) pkg> add MyPackage
 Resolving package versions...
 Installed MyPackage ─ v0.1.0
  Updating `~/.julia/environments/v1.0/Project.toml`
  [f41f8282] + MyPackage v0.1.0
  Updating `~/.julia/environments/v1.0/Manifest.toml`
  [f41f8282] + MyPackage v0.1.0
  [9a3f8284] + Random 
  [9e88b42a] + Serialization

julia> using MyPackage
[ Info: Precompiling MyPackage [f41f8282-a16f-11e8-2120-1f4619698387]

julia> MyPackage.greet()
Hello World!

無事に野良パッケージをパッケージ名だけで追加することに成功しました。 他の人の Genaral も ~/.julia/registries にクローンしてくれば同様にパッケージを追加することが出来ます。また、初回の git clone 以降はパッケージマネージャーが pull してきてくれます。

ちなみに

公式パッケージの情報が入っている ~/.julia/registries/General を消すと、パッケージマネージャーは自分のリポジトリしか見に行かなくなるので、世間から切り離された孤独感を味わうことが出来ます。

$ rm -rf ~/.julia/registries/General
$ julia -q
(v1.0) pkg> up
  Updating registry at `~/.julia/registries/MyGeneral`
  Updating git-repo `https://github.com/goropikari/MyGeneral.git`
 Resolving package versions...
  Updating `~/.julia/environments/v1.0/Project.toml`
 [no changes]
  Updating `~/.julia/environments/v1.0/Manifest.toml`
 [no changes]

おわりに

以上 Custom General Repository の作り方の紹介でした。お疲れ様でした。

この記事の内容を使えば、公式パッケージにしなくてもパッケージ名だけで追加できるようになりますが、みなさんには是非公式パッケージ作りにチャレンジしていただきたいです。

Julia 1.0 がリリースされてから、Julia を「使ってみた」といったコメントは目にするようになりましたが、「パッケージを作った」という報告は全然目にしない気がします。野良パッケージですらその状況なので公式パッケージではなおのことです。
私も他の方のパッケージ作りに興味がありますし、まだまだわからないことも多いので情報を共有していただけると嬉しく思います。

この記事で使った MyGeneral 等は以下です。必要ならばご自由にご覧ください。*5

github.com

github.com

github.com

*1:ここでは METADATAに登録されているものを公式パッケージ、されていないものを野良パッケージと呼ぶこととします。

*2:多分

*3:Compat.toml, Deps.toml, Versions.toml, Package.toml があるディレクトリ。

*4:MyPackage 中のディレクトリでない

*5:人に胸を張って見せられるようなコードではありませんが。

Julia - 野良パッケージを公式パッケージにする方法

先日、DiracNotation.jl という拙作が METADATA.jl にマージされ、野良パッケージから公式パッケージ*1へと昇格しました。 Julia 1.0 が出て以降では初めてパッケージを新規登録したので不安もあったのですが、とりあえず無事にマージされてほっとしています。

github.com

最近、METADATA.jl を覗いていると新規のパッケージ登録も多く見られるので、自分のパッケージも公式パッケージにしたくてウズウズしている Japanese Julian*2 もきっと多いことでしょう。

しかし、Julia 1.0 になって公式ドキュメントから公式パッケージへの登録方法が消え、またパッケージの管理方法も変わったので、どうしたら公式パッケージとして登録できるのか分からず困っている Julian も多いのではないでしょうか。

そこで今回は野良パッケージを公式パッケージに登録するまでの方法を紹介します。

(注) Julia 1.0 がリリースされてから1ヶ月以上経ちましたが、既存のパッケージの Julia 1.0 への対応だったり、Bug fix に追われているせいか、パッケージの作り方・登録の仕方に関しては後回しになって曖昧な状態になっていると感じます。 パッケージのテンプレートを作ってくれる PkgDev.jl もまだ Julia 1.0 に対応していませんし。
そのため、ここで書いたことが1週間後、1ヶ月後にも通用する保証はありません。

目次

環境

  • Julia 1.0

対象

本記事は以下の項目全てを満たす方を対象としています。

  • METADATA に登録をしたことがない Julian
  • Julia 1.0 向けのパッケージを作る Julian
  • Linux or Mac でも動くパッケージを作る Julian *3

必要なもの

  • 英語で質問された時に下手でも良いから伝えようとする気持ち
  • 自分のパッケージを公式パッケージにしても良いだろうという根拠のない自信
  • METADATA.jl に PR を投げる勇気

野良パッケージ作り

野良パッケージを作っていない人は、まずは野良パッケージを作ってください。大雑把な作り方に関しては過去の記事をご覧ください。

goropikari.hatenablog.com

基本的なパッケージ構成

以降では今回私が作った DiracNotation.jl を元に具体的に説明していきます。

パッケージの最小構成は以下のようになっています。

DiracNotation.jl/
├── .git
├── .gitignore
├── LICENSE.md
├── README.md
├── REQUIRE
├── src
│   └── DiracNotation.jl
├── test
│   └── runtests.jl
└── .travis.yml

Julia 1.0 が出る前に PkgDev.jl を使ってパッケージを作ったことがある人ならお気付きかもしれませんが、Julia 0.6 時代の構成と変わりません。

野良パッケージの時は必要なかった REQUIRE は必須で、野良パッケージの時に必須の Project.toml はなくても構いません。
(むしろ Project を JuliaCIBot は扱えないから入れないでと私より前に PR を投げていた人は言われていました*4。そんなこととは露知らず私は Project 込みで PR 投げましたが JuliaCIBot のテストも通ったし、特に何か指示されることもありませんでした。 なので結局 Project を入れて良いのか、入れないほうが良いのかよくわかりません。)

以前紹介したパッケージの作り方 に則ってパッケージを作った場合、LICENSE.md, README.md, REQUIRE, .travis.ymlが足りないので追加します。

LICENSE

ライセンスは適宜決めてください。Julia のパッケージの場合 MIT LICENSE を採用しているものが多いです。特にこだわりがない場合は MIT LICENSE にしておけば良いと思います。

ライセンスファイルは Github
Create new file --> ファイル名に LICENSE と打つと Choose a license template と出るのでクリック --> ライセンスを選ぶ
とすると簡単に作れます。

f:id:goropikarikun:20180920011848p:plain

f:id:goropikarikun:20180920011855p:plain

f:id:goropikarikun:20180920011902p:plain

README

作ったパッケージがどんなパッケージなのか README に軽く書きましょう。

Julia では、公式パッケージに登録する場合レビューが入るので README にどんなパッケージなのかの説明がないとレビュアーが困ってしまいます。

README に使い方を書くか、別枠で使い方のドキュメントをちゃんと作りましょう。 「ドキュメントがないと悲しくなる」と Udacity の人も言っていたので、ドキュメント大事。 www.udacity.com

Travis CI

f:id:goropikarikun:20180920014226p:plain

Julia の公式パッケージに登録する場合は JuliaCIBot *5 のテストは通せなくても Travis を通すのは必須です。 Travis でのテストが失敗すると「テスト失敗してるよ」とツッコミが入ります。

.travis.yml は外部ライブラリ*6に依存していない限り、大体こんな感じに書いておけば良いと思います。

language: julia
os:
  - linux
  - osx
julia:
  - 0.7
  - 1.0
  - nightly
matrix:
  allow_failures:
    - julia: nightly
notifications:
  email: false

テストできる OS は Linux または Mac です。必ずどちらかは入れてください。ここで Windows は使えません。

Julia の項目はパッケージが対応している Julia のバージョンを明示的に書いてください。release と書くと「明示的に書いて」とレビュアーから言われます。

REQUIRE

現状で一番面倒くさいことになっているのが REQUIRE です。Julia 1.0 向けのパッケージなのに Julia 0.x の作法に則らなければなりません。

野良パッケージ作成時に作った Project.toml のようなものなんですが少し違います。 以前の記事では例え標準ライブラリだろうとも Project.toml に書かなければならないと書きましたが、REQUIRE では逆に標準ライブラリを書いてはいけません。

依存している外部パッケージのみ REQUIRE自力で書いてください。Project.tomlと違って自動で作ってはくれません。 ここいう外部パッケージは公式パッケージに限られます。

今回私が作った DiracNotation.jlREQUIRE はこんな感じです。

julia 0.7
Requires

julia 0.7 と書くと Julia v0.7 以降の Julia を対象としているという意味になります。Julia 1.0 向けのパッケージだったら v0.7 を対応に入れなくても良いのですが、まだ Julia 1.0 に完全移行できていない人もいると思うので Julia 0.7 も対応に入れておくと良心的かなと思います。v0.7 = v1.0 + deprecation warning なので v0.7 を入れても問題ないはずですし。

外部パッケージの方にも適宜対応しているバージョンを書いてください。

私が初めて METADATA にパッケージを登録した時は「対応している外部パッケージのバージョンも書いて」と言われたのですが、最近はあまり言われていない印象です。レビュアーによってその辺のさじ加減は違うのかなという気はします。 ただ、メジャーバージョンが上がる前のパッケージでしか動かないなどという場合はちゃんと REQUIRE にバージョンも書きましょう。そうしないとそもそも travis で失敗すると思いますが。

対象とするバージョンの詳しい書き方は v0.6 時代のドキュメントを読んでください。

Packages · The Julia Language

パッケージの名前

公式パッケージにする場合、パッケージの名前にも注意を払う必要が有ります。

とりあえず、

  • 略語を入れない
  • パッケージ名に Julia という単語を入れない
  • アッパーキャメルケースにする (私のパッケージの場合、Diracnotation でなく DiracNotation にする)

ということを守っておけばパッケージ名に関しては何も言われないのではと思います*7

詳しいことは v0.6 時代のドキュメントを読んでください。

Packages · The Julia Language

METADATA に PR を投げる

全ての準備は整いました。あとは PR を投げるだけです。 自力で PR を投げる場合、METADATA.jl を fork して編集して・・・ とやるのですが、今は attobot という便利なツールがあるのでこれを使いましょう。むしろ自力で PR を投げると「次回からはattobot使ってね」と言われるので初めから attobot 使いましょう。 attobot を使うと Github 上でバージョンタグを付けると自動で METADATA へ PR を投げてくれます。

github.com

https://github.com/integration/attobot にアクセスすると、attobot をまだインストールしていない場合、Install 画面が出ると思うのでインストールしてください。

そのあと、Configure -> Select repositories -> METADATA に PR 投げたいパッケージを選ぶ -> Save

f:id:goropikarikun:20180920092203p:plain

f:id:goropikarikun:20180920092211p:plain

attobot を入れたら Github 上でバージョン番号でタグ付けします。バージョン番号はセマンティック バージョニング に従ってください。以前は初回リリース時は v0.0.1 と付けるのが普通でしたが、最近は v0.1.0 から始めるようです。

タグ付けすると自動的に METADATA.jl へ PR が投げられます。私のような OSS 開発素人は一番緊張するところです。

semver.org

f:id:goropikarikun:20180920093313p:plain

f:id:goropikarikun:20180920093322p:plain

f:id:goropikarikun:20180920093331p:plain

Publish release をクリックしたら自動的に METADATA.jl に PR が投げられます。こんな感じ。

f:id:goropikarikun:20180920093758p:plain

30秒経っても PR が作られない場合、「PR が作られないのだけれど。。。」と attobot のリポジトリに issue を立ててください。中の人が対応してくれます。

私は一度だけ PR が出来なかったことがあるのですが、その時は Travis が終了する前に PR を投げたのでそのせいなのかなぁと思いました。真相は結局わかっていませんが 。

あとは静かに3日待ちます。投げた PR に3日間コメントがないと自動的にその PR はマージされるらしいです*8 *9*10

あなたの PR よりも後に出した PR の方が先にマージされることがありますが文句を言ってはいけません。すでに公式パッケージになっているもののバージョン上げの場合、速攻マージされます。新参者は静かに待ちましょう。

特に問題がなければ以下のようにマージされます。☆-ヽ(*´∀`)八(´∀`*)ノイエーイ

f:id:goropikarikun:20180920095554p:plain

パッケージ名だけで追加できちゃう(σ・ω・)σYO♪
公式パッケージのみに許された特権だ (σ≧∀≦)σчоッ☆ f:id:goropikarikun:20180920104805p:plain

おわりに

いかがだったでしょうか?公式パッケージにするまでの方法の雰囲気はつかめましたでしょうか?

Julia の公式パッケージ作りは全て英語でやりとりしないといけないので、私のような英語が苦手な人にとってはなかなか大変ですが、苦労した分 公式パッケージとしてリリースできたときの達成感は何ものにも代え難いです。

私のような素人 Julian でも出来るのですから、皆さんも気楽に Let's try!!!

パッケージ作りでわからないことがあったら公式の Discourse か Slack で聞いてください。

Julia Community

余談

パッケージを作った当初は公式パッケージにするつもりはなかったのですが、QuantumOptics.jl の gitter にて JuliaDiffEq で有名な Rackauckas さんに私の野良パッケージへのリンクを貼っていただき、まわりの反応もそんなに悪くなさそうだったので公式パッケージにしてもいいかなぁという気になりました。

f:id:goropikarikun:20180920110430p:plain

また、前々から外部パッケージに依存しないパッケージを作りたいなぁと思っていたのと*11、パッケージを Julia 1.0 でも動くように直したら「公式パッケージにしたいなぁ」という謎の衝動に駆られたので公式パッケージとしてリリースすることにしました。

正直に言って pretty-print は自己満足の世界ですし、なくても困らないのでこのパッケージが使われることは殆どないと思いますが、パッケージのリリースアナウンスツイートが Julia 創始者の一人である Viral B. Shah さんからリツイート & いいね をもらえたのでそれだけで私は満足です。

*1:ここでは METADATA.jl に登録されているパッケージを公式パッケージ、登録されていないパッケージを野良パッケージと呼ぶこととします。

*2:Julian = Julia user

*3:Linux だけ、 または Mac だけで動くパッケージの場合はこの記事の内容で大丈夫ですが、Windowsだけで動くものを登録したい場合はこの記事の内容が通用しません。

*4:Register new package Catsay.jl v0.1.0 by attobot · Pull Request #17687 · JuliaLang/METADATA.jl · GitHub

*5:METADATA に PR 投げた時のみに走るテスト

*6:Juliaのパッケージでなく、C 言語のライブラリとかのこと

*7:代替案を出されることも有ります。

*8:Tag Reinforce.jl v0.1.0 by attobot · Pull Request #18098 · JuliaLang/METADATA.jl · GitHub

*9:Register new package DrawSimpleGraphs.jl v0.1.0 by attobot · Pull Request #18043 · JuliaLang/METADATA.jl · GitHub

*10:私は、この「3日で自動的にマージする」という規則を知らなかったので、「回りの人たちにはレスポンスがあるのに何故私のところには何もないの。。。自分でも謎のパッケージ作ったなという自覚はあるけど、コメントするのも憚られるほど!?」と不安になったのですが、コメントがあると自動マージ機能が無効になるというコメントを見て、「むしろ私のパッケージ優等生じゃん!」と開き直れました。

*11:依存があるとパッケージの読み込みが遅くなるし、メンテナンスが面倒

NiZ Plum 75 を無刻印化

格安静電容量無接点式キーボード NiZ Plum 75 を購入してから2ヶ月弱経ちましたが概ね満足しています。 しかし、一つ問題があります。それはキートップに印字があること!

単純に無刻印に憧れがあったということもありますが、私は Dvorak 配列を使っているので印字にほとんど意味がありません。 というか下手に書いてあるせいでたまにキーボードみると書いてあることに引っ張られます。 そんなわけで、米Amazonから無刻印のキーキャップを買って換装しました。

注文から到着まで

購入したのは以下の黒色です。送料込みで $23.49 でした。手数料込みで3000円弱ですかね。

www.amazon.com

9月10日に注文し、到着予定日は10月4日〜10月26日。
1ヶ月以上も掛かるのかぁと思っていたら配送激遅で有名な China Post じゃありませんか。そりゃ遅い。 前 China Post から送られてきたものは到着予定日過ぎて2日後くらいに届いたので、11月初めまで待つ感じになることも覚悟したのですが、結果的に8日で届きました。

f:id:goropikarikun:20180918124003p:plain

f:id:goropikarikun:20180918124634j:plain 箱はべこべこです。

f:id:goropikarikun:20180918124653j:plain 箱がこれなので、中身もスーパーの袋みたいなのにごちゃごちゃに入っているのかと思いましたが、ちゃんと一列ずつ分かれている袋に入れられていました。

f:id:goropikarikun:20180918131532j:plain 左Ctrlの比較。元々付いていたキーキャップよりも気持ち高い。 キーの触り心地は元々付いていたものとあまり変わりませんでした。

キーキャップ換装

元のキーキャップを取っていきましょう。私はFilcoのキーキャップ外しを使用しました。 全換装した後に思い出しましたが、NiZ Plum 75 には キーキャップ外しが標準で付いてくるので買う必要はありません。ただ、スペースキーのように長いものは標準でついてくる工具の方が外しやすいですが、その他のキーに関しては Filco のタイプの方が外しやすいと思います。

f:id:goropikarikun:20180918125123j:plain

NiZ のキーボードちょいちょいバックライトが光る部分があるのですが、どうやら Esc, Caps Lock, 左Ctrl, F10, F11, F12 が光るようです。未だに光る条件がわかりませんが。特に最近 Esc がどういうわけかよく光ります。

f:id:goropikarikun:20180918130745j:plain

後は単純に挿していくだけです。キーの抜き差しは元々のキーよりも重くなりました。 それとスペースキーの滑りがよくありません。力入れて差し込むとキーが下がったまま戻ってきません。若干引き戻すと一応戻ってくるようになりました。だけど動きが渋いです。一応隣のキーと干渉しているわけではなさそうなのでスペースキーのサイズに問題はないと思うのですが。スタビライザーについていたグリスをベタベタ触ってしまったのでグリスが落ちてしまったのかもしれません。

f:id:goropikarikun:20180918131355j:plain

全換装終了。なんだかんだで40分ほどかかりました。 キーキャップが若干高くなった分打ち心地が変わると思ったのですが、ほとんど感じませんでした、スペースキー以外は。。。 私は SandS (Shift and Space) を使っているので普通の人よりスペースキーの使用頻度はものすごく高いです。そのためスペースキーの違和感は結構ネックになってきます。

f:id:goropikarikun:20180918131425j:plain

試しに元のスペースキーに戻したら、動きもスムーズだし打ちやすかったのでスペースキーだけ元のものにしました。一応無刻印は保っているので良しとしましょう。*1

見た目は正直言ってダサいですね。キーの汚れが気になると思って黒色にしましたが、外枠が白なのでそれに合わせて真っ白にしたほうがよかったかなぁとは思います。

キーボード本体と今回買ったキーキャップを合わせても1万円ちょっとで無刻印の静電容量無接点方式のキーボードが買えたのだから贅沢は言えません。

*1:サイドにNiZ Plum のロゴはありますが。

Julia - Jupyter notebook と Julia のソースコードの相互変換方法

Jupyter notebook って便利ですよね。私も数式の入ったメモを入れたい場合や、コードとプロット結果を一緒にしたいときにはよく利用しています*1

ただ一つ問題があって git との相性が恐ろしいほどに悪いことで有名ですよね。たった1行コードを追加しただけでも notebook の内部はそれ以上に変化してしまうので、git で管理しようにもどこに変更が入ったのか全然わかりません。

折衷案として notebook をソースコードに変換してそれを git で管理すると言う方法があります。

notebook のままだと1行追加するだけでも jupyter を起動せねばならず私としては億劫なのですが*2ソースコードなら編集も気軽にすることができます。しかし、nbconvert では notebook --> ソースコードの変換はできても、ソースコード --> notebook の変換ができないことが悩みの種でした。

そんなところに彗星のごとく現れた救世主が今回紹介する Literate.jl です。これを使うことで Julia のソースコードから notebook を生成することができます。README を読んだ時はいまいちイメージできなかったのですが、実際に notebook が作成されると感動しました!

github.com

以下、$ で始まるコードブロックは bash での実行を、julia> で始まるコードブロックは Julia の REPL での実行を表します。

目次

環境

julia> versioninfo()
Julia Version 1.0.0
Commit 5d4eaca0c9 (2018-08-08 20:58 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Xeon(R) CPU           E5645  @ 2.40GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.0 (ORCJIT, westmere)
Environment:
  JULIA_SHELL = /usr/bin/zsh

インストール

Literate.jl をインストール

julia> using Pkg; Pkg.add("Literate")

notebook からソースコードを生成する時に jupyter_contrib_nbextensions も必要らしいので入れます。

$ conda install -c conda-forge jupyter_contrib_nbextensions

github.com

使い方

Julia のソースコードを notebook に変換

まずはサンプルとして以下の内容を example.jl として作ってください。

# # Hello World
# こんにちは

x = 1
println("Hello World!")

ソースコード上でコメントになっているところが markdown cell になり、コードの部分が code cell になります。

Julia を起動し、以下のコマンドを実行してみましょう。

julia> using Literate

julia> Literate.notebook("example.jl", "."; documenter=false, credit=false, execute=false)

すると、example.ipynb という notebook がカレントディレクトリにできるはずです。 中身を見てみましょう!

f:id:goropikarikun:20180902223710p:plain

どうですか!ちゃんと notebook が生成されましたよ!!!

先程は executefalse にしましたが、ここを true にすると実行までされた notebook が生成されます。

julia> Literate.notebook("example.jl", "."; documenter=false, credit=false, execute=true)

f:id:goropikarikun:20180902223851p:plain

#- を入れると cell を区切ることが出来ます。

example.jl

# # Hello World
# こんにちは

x = 1
#-
println("Hello World!")

f:id:goropikarikun:20180902223919p:plain

Julia のソースコードmarkdown に変換

LIterate 使うと Julia のソースコードから markdown の生成も出来ます。

Literate.markdown("example.jl", "."; documenter=false, credit=false)

f:id:goropikarikun:20180902223911p:plain

notebook を Julia のソースコードに変換

心を新たにし、example.jl はまだ無いとします。
notebook から Julia のソースコードへの変換は "Download as" で Julia を選べばOK♪...では無いんです。残念。。。

f:id:goropikarikun:20180902233358p:plain

実際ダウンロードしてみればわかりますが、下記のように markdown の内容が消失します。

x = 1

println("Hello World!")

Python をダウンロードする時はちゃんと markdown も残ってるんですけど、Julia の場合だと消失します。 IJulia にあった issue によると nbconvert の問題だそうです。

github.com

うまいことテンプレートを作ると良いらしいのですが、ドキュメントを読んでもよくわからなかったので、上のリンク中で Carreau さんが提示されているテンプレートを使わせていただきました。
Customizing nbconvert — nbconvert 5.3.1 documentation

julia.tplを新規作成

{%- extends 'script.tpl' -%}

{% block markdowncell scoped %}
{{ cell.source | comment_lines(prefix='# ') }}
{% endblock markdowncell %}

notebook を Julia のソースコードに変換

$ jupyter nbconvert --to script 'example.ipynb' --template=julia.tpl

すると example.jl というファイルが出来ます。中身はこんな感じ。

# # Hello World
# こんにちは

x = 1

println("Hello World!")

変換されたこの書式はまさに Literate.jl で notebook に変換できる書式そのものではありませんか!!!

問題点

notebook --> ソースコードソースコード --> notebook と相互変換が出来るようになりましたが、完全に可逆というわけではありません。

ソースコード --> notebook の変換の時 #- を入れると cell を分割することが出来ましたが、notebook --> ソースコード では分割を表すような記号が入らないので notebook --> ソースコード --> notebook と変換をすると cell の分割が消えます。テンプレートで回避できそうな気がしますが、私は Jupyter の構造に詳しくないので解決策はわかりません。

おわりに

Literate で変換するソースコードを git で管理すれば、間接的に notebook を git で管理していることになるので大分 notebook のバージョン管理がやりやすくなるのではと思います。

Python だと gitnb というのを使うと notebook を git で管理しやすいようです。
GitHub - brookisme/gitnb: git tracking for python notebooks

*1:パッケージ作るときはもっぱら Atom 使っています

*2:エディタで直接編集することもできますが、編集箇所を探すのが大変

Julia - 関数に型そのものを渡す

環境

julia> versioninfo()
Julia Version 1.0.0
Commit 5d4eaca0c9 (2018-08-08 20:58 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i5-4460T CPU @ 1.90GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.0 (ORCJIT, haswell)
Environment:
  JULIA_SHELL = /bin/bash
  JULIA_EDITOR = nvim

型そのものを関数に渡す

整数型の変数とかではなく Int とか型そのものを引数に取る関数を作りたい場合、

hoge(::Type{Int}) = println("Hello World!")

の様に引数にとりたい型を Type でくるんでやればいいらしい。

julia> hoge(::Type{Int}) = println("Hello World!")
hoge (generic function with 1 method)

julia> hoge(Int)
Hello World!

julia> hoge(Float64)
ERROR: MethodError: no method matching hoge(::Type{Float64})
Closest candidates are:
  hoge(::Type{Int64}) at REPL[1]:1
Stacktrace:
 [1] top-level scope at none:0

Julia を使い始めて以来、pretty-print (Base.show(io::IO, ::MIME"text/plain", z::Polar{T}) とか)に出てくるMIMEのところは仮引数がないけど何を渡しているんだと思ってきたのですが、型を渡しているのだと考えると色々と合点がいく気がします。

Julia のドキュメント中に型そのものを渡す方法の記述が見つからない*1ので保証はしませんが。randソースコードを見ると合ってる気がするようなしないような。

参考

*1:Type で検索するとノイズが多すぎてあるのかどうかわからない

Julia - Julia のパッケージ作り

目次

TL;DR

  • 野良パッケージはどんどん作ろう!
  • 新規の METADATA デビューは今は待ったほうが良いかなぁ。。。

環境

Julia Version 1.0.0
Commit 5d4eaca0c9 (2018-08-08 20:58 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i5-4460T CPU @ 1.90GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.0 (ORCJIT, haswell)
Environment:
  JULIA_SHELL = /bin/bash
  JULIA_EDITOR = nvim

自作のパッケージ

オリジナルパッケージの雛形を作る

(v1.0) pkg> generate MyPackage # 自作のパッケージの雛形を作成
Generating project MyPackage:
    MyPackage/Project.toml
    MyPackage/src/MyPackage.jl

shell> cd MyPackage/
/home/arch/tmp/MyPackage

(v1.0) pkg> activate .

(MyPackage) pkg> 

自動的に出来るファイルの中身
src/MyPackage.jl

module MyPackage

greet() = print("Hello World!")

end # module

この module の間にずらずらプログラムを書いていくと晴れて自分のパッケージが完成します。 詳しくは公式ドキュメント Modules · The Julia Language を読んでください。

試しに作ったパッケージを読み込んでみます。

julia> using MyPackage
[ Info: Precompiling MyPackage [f41f8282-a16f-11e8-2120-1f4619698387]

julia> MyPackage.greet()
Hello World!

関数を追加する

src/MyPackage.jl

module MyPackage

greet() = print("Hello World!")
hello(io::IO, name) = print(io, "Hello ", name)
hello(name) = hello(stdout, name)

end # module

Julia を立ち上げ直して、もう一度パッケージを読みこみ直す。

(v1.0) pkg> activate .

(MyPackage) pkg> 

julia> using MyPackage
[ Info: Recompiling stale cache file /home/arch/.julia/compiled/v1.0/MyPackage/o5PoH.ji for MyPackage [f41f8282-a16f-11e8-2120-1f4619698387]

julia> MyPackage.greet()
Hello World!
julia> MyPackage.hello("Mike")
Hello Mike

テストを書く

テストケースは test/runtests.jl に書いていきます。@test 後が false になるとテストを通すのに失敗します。

using Test, MyPackage

@test sprint(MyPackage.hello, "Mike") == "Hello Mike"

Project.toml を編集。テストだけに使うパッケージは [extras][targets] に書きます。

name = "MyPackage"
uuid = "f41f8282-a16f-11e8-2120-1f4619698387"
authors = ["goropikari <goropikari56@gmail.com>"]
version = "0.1.0"


[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]

現在のディレクトリ構成はこんな感じ

shell> tree
.
├── Project.toml
├── src
│   └── MyPackage.jl
└── test
    └── runtests.jl

2 directories, 3 files

テストする。 tests passed と出れば OK。

(MyPackage) pkg> test
   Testing MyPackage
 Resolving package versions...
   Testing MyPackage tests passed

自分のパッケージの中で他のパッケージを使う場合

自分のパッケージの中で Random モジュールを使う例を通して v0.6 までとの違いを紹介します。 例えば、以下のようにプログラムを書いたとします。

MyPackage.jl

module MyPackage

using Random

greet() = print("Hello World!")
hello(io::IO, name) = print(io, "Hello ", name)
hello(name) = hello(stdout, name)
randstr() = randstring()

end # module

Julia v0.6 までは、今回の Random module のように Julia に標準実装された module に関しては REQUIRE *1 に書かずとも自分のパッケージ内で using してもその module を普通に利用することができました。

しかし、Julia v1.0 からは例え標準モジュールであろうとも using または import で読み込む module は Project.toml に書かなければならなくなりました。なかなか面倒な仕様になったのですが、幸いこれらを自分で Project.toml に書く必要はありません。add でこれらの module を追加すると自動的に Project.toml が更新されます。

(MyPackage) pkg> add Random
  Updating registry at `~/.julia/registries/General`
  Updating git-repo `https://github.com/JuliaRegistries/General.git`
 Resolving package versions...
  Updating `Project.toml`
  [9a3f8284] + Random 
  Updating `Manifest.toml`
 [no changes]

Project.toml が自動的に更新される。

name = "MyPackage"
uuid = "f41f8282-a16f-11e8-2120-1f4619698387"
authors = ["goropikari <goropikari56@gmail.com>"]
version = "0.1.0"

[deps]
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]

ついでに Manifest.toml というのが出来きますが、このファイルにはテストしたときの全てのパッケージ情報が書かれていています。Project.tomlManifest.tomlという2つの軽量なテキストファイルを人に渡すだけで、自分がテスト通したときの環境がそっくりそのまま他の人のマシン上でも再現できるので、再現性が重要になってくる分野では便利でしょとのこと*2

関数を追加したのでテストケースも追加します。

using Test, MyPackage, Random

Random.seed!(0)

@test sprint(MyPackage.hello, "Mike") == "Hello Mike"
@test MyPackage.randstr() == "0IPrGg0J"

もう一度テスト

(MyPackage) pkg> test
   Testing MyPackage
 Resolving package versions...
   Testing MyPackage tests passed 

UUID

今までスルーしてきましたが Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" などの謎の文字列は各パッケージのUUIDです。 Generalを見れば調べられますが*3、探すの面倒なので上記の ]add Package 使って入れていくのが吉。ただ、add で入れると全部 [deps] のほうに入ってしまうのでテストだけにしか使わないものは適宜 [extras] の方に自分で移し [targets] も編集します。

GitHub に公開する

作ったパッケージを野良パッケージ*4として GitHub に公開してみましょう!

まずは GitHub に新しくリポジトリを作ります。Julia のパッケージの慣例としてパッケージ名の最後に .jl をつけるので今回は MyPackage.jl とします。(慣例で .jl と名前をつけますが、これをつけておくと確か野良パッケージでも Julia Observer に見つけてもらえます。)

作ったら以下を実行。もちろん GitHub のアカウントはご自身のものに直してください。

$ cd /path/to/MyPackage
$ git init
$ git add -A
$ git commit -m 'first commit'
$ git remote add origin https://github.com/goropikari/MyPackage.jl
$ git push -u origin master

これで野良パッケージの公開終了。今後は以下のようにして誰でもインストールできるようになります。

(v1.0) pkg> add https://github.com/goropikari/MyPackage.jl

適宜ライセンスやREADMEも加えましょう。

MyPackage に PATH を通す

作ったパッケージはディレクトリを activate していると using などで読み込めますが、そうでない一般の時は PATH が通っていないので読み込むことが出来ません。

julia> using MyPackage
ERROR: ArgumentError: Package MyPackage not found in current path:
- Run `Pkg.add("MyPackage")` to install the MyPackage package.

Stacktrace:
 [1] require(::Module, ::Symbol) at ./loading.jl:817

バリバリ開発しているときは activate すればよいですが、一段落付いて普通のパッケージとして使いたい時は下記のようにして PATH を通します。

julia> push!(LOAD_PATH, "/path/to/MyPackage/")

こうすると他のパッケージ同様、どこのディレクトリにいても読み込むことが出来ます。

julia> using MyPackage

julia> MyPackage.greet()
Hello World!

ただこのままだと 毎度、毎度 PATH を通さなければならないので ~/.julia/config/startup.jl追記しておきましょう。

push!(LOAD_PATH, "/path/to/MyPackage/")

METADATA.jl に登録する

作ったパッケージを Julia のパッケージとして登録*5したい場合は Travis CI を通すことが必須です。 PythonPyPI ではコードレビューが無いらしい*6ですが Julia の場合はあるので、print("Hello World") しかないようなパッケージを登録しようとすると却下されます。

README またはドキュメントもちゃんと書きましょう。当たり前ですが英語で*7。 ドキュメントが書かれていないパッケージはいかに優れていたものだったとしても使う気になりません。

Julia v0.6 まではパッケージの命名に関する決まりごと*8等もドキュメントに書いてありましたが、現時点 (2018/8/17) の Julia v1.0 のドキュメントではパッケージの登録方法の項目が見当たらないので、どういうルールになったのかはよくわかりません。概ね一緒だと思いますが。

登録をするときは AttoBot を使うことが推奨されていますが、現状これを使う場合は v0.6 の作法の REQUIRE というファイルを作らなければなりません。Pkg3用に attobot3 というのもあるようですが、従来の attobot と同様にして使えるのかは不明。

パッケージの雛形を作ってくれる PkgDev.jl*9 がまだ Julia v1.0 に対応しておらず、また、Julia v1.0 が急にリリースされたせいで以前から Julia のパッケージを作っていた古参 Julian も困惑している感じが否めないので新規の METADATA デビュー *10 は当面控えたほうがよさそうといった印象です。

それでも「いますぐ私の素晴らしいパッケージを世に広めたいんだ!」という方は Julia Discourse や Julia の Slack であたりで質問してください。私は従来の方式でしかパッケージ公開をしたことないのでアドバイスできるようなノウハウがありません。

discourse.julialang.org

参考

*1:現在のProject.tomlに相当

*2:https://youtu.be/GBi__3nF-rM

*3:標準モジュールについてはありませんが

*4:METADATA.jl に登録されていないパッケージ

*5: add PackageName でインストールできるようになる

*6:PyPI 使ったことがないので確証はありませんが

*7:今Julia使っている人の英語力なら特に問題ないはず

*8:https://docs.julialang.org/en/v0.6.4/manual/packages/#Creating-a-new-Package-1

*9:Travis とかのテンプレも作ってくれる

*10:今後は General デビューというべきなのかもしれない

安価な静電容量無接点方式キーボード NiZ Plum 75 を買ってみた

購入まで

私は普段キーボードは HHKB Lite2 と MiSTEL BAROCCO MD600 (赤軸) を使っています。 MD600 は今年の4月より使い始めましたが、最近は MD600 の押下圧に慣れてきたせいで HHKB の押下圧がすごく重く感じるようになってきました*1。 そのせいで HHKB で長時間タイピングをしていると手が痛くて痛くてしょうがない。

PFU Happy Hacking Keyboard Lite2 英語配列 USBキーボード ブラック PD-KB200B/U

PFU Happy Hacking Keyboard Lite2 英語配列 USBキーボード ブラック PD-KB200B/U

気持ちとしてはもう一つ MD600 が欲しいのですが 1.5万円くらいするので気安く買おうとも思えません。 だからといって HHKB の重さは結構なストレス。

どうしたものかと考えているある日、NiZ*2 というところが1万円ちょっとで静電容量無接点方式のキーボードを売っていることを知りました。 Amazonのレビューを信じる限り値段の割にはなかなか良いものらしい。

静電容量無接点方式といえば HHKB Professional や東プレ RealForce などのハイエンドモデルに採用されている構造で有名ですが、それが1万円ちょっとで買えるというのだから興味がそそられました。

私の理想としては

という条件を満たしたキーボード*3があったら嬉しいのですが、いかんせんそのようなキーボードは見たことも聞いたことも無い。

そんなこんなで MD600 をもう一個買うか、はたまた NiZ Plum 75キー*4 を買うかと迷っているときに NiZ Plum 75キー がAmazonタイムセールで8,479円で売っているのを発見。 しかも Keepa によるとこの1年間での最安値!!!さらにキャンペーンで666円引きだったので7,813円に。
いくら中華キーボードとは言え新品の静電容量無接点方式のキーボードが8000円切る値段で買えるなんて!!!

タイムセール終了まで残り時間が少なかったこともあり勢いで購入してしまいました。

f:id:goropikarikun:20180725001505j:plain HHKB Lite2 との比較。久々に独立したファンクションキーがあるキーボードを触った。

f:id:goropikarikun:20180725001514j:plain Caps Lock が有効になっていると緑色に光る。私が買ったモデルはRGBではないが何箇所か同様にバックライトがある模様。だが光る条件とどこが光るのかはよくわからない。

打ち心地

私は静電容量無接点方式のキーボードを初めて買いましたが、スコスコとした打ち心地がいいですね♪ HHKB Professional 等と比べると安っぽさは否めませんが、値段も半額以下と言うことを考慮すれば十分優秀です。

一番私が気にしていた押下圧について、私の感覚ではCherry 赤軸も相当軽いと思っていましたがそれを超える軽さです。ここまで軽いとは思いませんでした。今まで使ってきたHHKB Lite2 が重かったこともあり、指を置くだけで入力ができるかのようです。 まだ1日しか使っていませんがこちらの打ち心地が軽すぎて赤軸がすごく重く感じてしまうようになってしまいました。

ファームウェアのアップデート

キーボードの入っている箱にキーボードの取説とキー配置を変更するためのソフトをダウンロードするためのQRコードがついていますが、ソフトのバージョンが古かったのでメーカーのサイトからダウンロードしました。*5

Download – PLUM_NIZ keyboard

キー配置を変更

このキーボードはプログラマブルなのでキー配置を変えることができます。*6

キー配列を変えるためのソフトの使い方が最初よくわからなかったので、何回かタイプしても全く反応しなくなるという現象に陥りなかなか焦りました。

特に Fn の割当方法は他のキーの割当方法と違いわかりづらかったです。
f:id:goropikarikun:20180725020201p:plain
CapsLockをCtrlとかに割り当てる時はこちらを使う。しかし、Fn の場合押しても何にもならない。

f:id:goropikarikun:20180725020211p:plain
Fn を他のキーに割り当てたい場合、上のタブの Function を選び、その中に表示される FN マークのアイコンを選択。そうすると Fn キーの割当を他のキーにすることができる。

とりあえず私は日頃使っている MD600 と同じように設定しました。

私の設定

Before -> After

  • CapsLock -> Ctrl
  • Back Space -> \
  • \ -> `
  • 左ALT -> Fn
  • Fn -> Win
  • Fn + J -> ←
  • Fn + K -> ↓
  • Fn + L -> →
  • Fn + I -> ↑
  • Fn + ; -> Back Space
  • Fn + ' -> delete
  • Fn + H -> Home
  • Fn + N -> End

あとは全体的にアルファベットをDvorak配列と同じになるように割り振りました。
割り振るのは大変でしたが MD600と違ってカスタマイズしたキー配列の情報を .pro という拡張子のテキストファイルに保存できるので便利です。 デフォルトのProモードのキーレイアウトをDvorakに変更した(他のキーはそのまま)キーレイアウトファイルをGist に上げたのでDvorakに変更したい方はご自由にお使いください。
Dvorak layout for NiZ Plum 75

変な設定をしてしまいよくわからなくなっても、 最悪 ESC + Left Ctrl + Delete + Right Ctrl を5秒以上同時押しすれば Pro モードの配列が初期化されるので元にもどります。

私はこのキーボードを自宅で使うので Dvorak配列にしても誰に文句を言われることもありませんが、職場で使う場合はDvorak配列知らない人に打ってもらうことなどもあるかもしれません。そんなときはキーボード裏のスイッチでモードを Offce か Media に変更すれば、QWERTY になるので他の人に打ってもらう機会があっても問題ありません!

参考

Happy Hacking Keyboard | 高品位キー | PFU Plum 75 Keys 35g/45g Electro-Capacitive Keyboard Non RGB backlighting – PLUM_NIZ keyboard

追記

キーキャップを変えて無刻印化しました。 goropikari.hatenablog.com

*1:メーカーのデータによると HHKB Lite2 の押下圧は約 55 g、Cherry MX赤軸 は 約 45 g らしいので約 10 g の差。

*2:会社名なのかブランド名なのかよくわかりません。

*3:MD600が静電容量無接点方式になったら最高

*4:おそらく正式な名称はPlum 75 Keys 35g/45g Electro-Capacitive Keyboard Non RGB backlighting,

Plum 75 Keys 35g/45g Electro-Capacitive Keyboard Non RGB backlighting – PLUM_NIZ keyboard

*5:QRコードのリンク先のバージョンはv1.1.30. メーカーサイトに上がっているものは v1.1.35 だった。

*6:Windows