Perlでスマートに配列からランダムで特定個数の値を取得する

TL;DR

use List::Util qw(sample);

my @array = (1..100);
# 3個ランダムでとりだす

my @random_pickup = sample 3, @array;

人間生きていると配列の中身からランダムにn個取得したいときがあります。Perl書いているときもありますね。

よくある方法は次のような感じでしょうか

my @array = (1..100);
my @random_array =  shuffle @array ;
my @random_pickup  = splice @random_array, 0, 3;

List::Utilのshuffleを使い配列をランダムにばらしたあとに、標準関数のspliceを使い特定の数の値を取り出してきます。 これだと@random_pickupには3つのランダムな数が入る訳ですね。

ただしこの方法だと必ず全件をshuffleしないといけないため、巨大な配列を対象にする場合はそこそこコストが掛かります。 他には結局@random_pickupが必要な場合は、中間変数の@random_arrayが不必要ですね。 (最も、工夫すれば中間変数を作らずにかけますが...)

そこでオススメなのがList::Utilのsample関数です。1.54から同梱されているので、比較的最近のList::Utilになら入っています。 ちなみにList::MoreUtilsにも入っています。

書き方は簡単で以下の感じです。

my @items = sample $count, @values; # sample {取得したい数}、 {リスト}

$count@valuesの配列長より大きい場合は、shuffleと同じ挙動になります。

近年のList::UtilはXS(C言語実装)も同梱されており、sampleも見てみるとC言語レベルで特定個数のランダム取得をしてくれています。 このため、コスト的にもshuffleしてからspliceなどで取得するより最適化されておりオススメです。 metacpan.org

2022年、CPAN(Perlの)モジュールのメンテナを引き継ぐ活動を始めた件

これははてなエンジニアアドベントカレンダー2022 42日目の記事です。 昨日は id:k-murakami0609 さんの 過去に所属してたチームに転生したら導入したいもの でした。

はてなのノベルチームで日常的に使っている便利グッズ最高ですね!! みなさんもノベルチームにjoinして体験してください!!!

さて今回は2022年にぼちぼち始めたCPANモジュールのメンテナを引き継ぐ活動についてお話しようかなと思います。

CPANモジュール

CPANモジュールとはご存知プログラミング言語Perlのモジュールシステムのことです。

Perlインタプリタに付随しているコアモジュールも含めて、PerlではCPANと呼ばれるアーカイブにモジュールがアップロードされ、cpanmcpmなどのツールを通してインストールし利用する世界観になっています。

TeXのモジュールアーカイブのCTANに影響されて作成されたと言われていることからも分かる通り、CPANは1995年から存在しており、歴史的にも古いシステムになっています。

そんなCPANには大小様々なモジュールがアップロードされています。特に有名どころではAcme名前空間でしょうか。 他の言語に無い特徴ですが、PerlのモジュールではAcme::と名前がつくモジュールはジョークモジュールとなっていて、様々なおもしろモジュールがアップロードされています。(どういう物があるか知りたい方はAcme大全がオススメです)

CPANモジュールの今

そんなCPANモジュールですが、Perl使用者の人口減に伴って、新規モジュール開発及びモジュールのメンテナが減少傾向にあります。

最近ではElasticsearchの公式クライアントライブラリのSearch::ElasticsearchもElasticsearch8を持ってPerlクライアントの提供を終了することを発表したりしています。

日本でもPerlを書かれていたエンジニアの方々が、別の言語にコミットすることになったり、また多忙でPerlを書く時間が取れなかったりと、様々な理由でメンテナが減少しています。 使わないモジュールのメンテナンスをし続けるのはなかなかしんどいですよね。

Perlではここ数年@INCがカレントディレクトリから削除されたことなどもあるため、継続してメンテナンスをしていくのは後方互換性が強いPerlといえども必要そうです。

とはいえ、はてな社にはまだPerlで元気に動いているPerlプロダクトがありますし、何よりid:anatofuzPerlが好きなので、できればモジュールのメンテは継続していきたいです。

他にはCPANモジュールではないですが、Perlの公式Dockerイメージの管理もごく数人で行っているので、こういったところにもコミットしたいなと思っていました。

CPANモジュールのメンテに関わる活動

そこで去年はパッチを当てたいOSSを中心にメンテナを引き継ぐ活動をしました。 そこまで件数が多いわけではなかったですが、声を書けながらメンテ権を頂いています。

溜まっていたPRの解決や、新規PRのレビュー等に参加することで、継続してメンテナンスを行っています。

github.com

直近ではTest::WWW::Stubのメンテに関わりました。

metacpan.org

モジュール以外ではdocker-perlやOpenAPIのスキーマからクライアントコードを自動生成するOpenAPI GeneratorのPerlクライアントもPRを送っています。

github.com

github.com

CIの修正なども

他にはPerlモジュールは、日本でよく使われるCPANモジュールオーケストレーションツールのMinillaが標準でTravis CI用の設定ファイルを出力していたことから、Travis CIでCIを回しているものが多かったです。

しかしTravis CIは近年では動かなかったりするので、これをGitHub actionsに移動するなどの活動もメンテ権をもらうついでにシュッと進めています。

github.com

CPANモジュールのメンテを引き継ぐ方法

CPANモジュールのメンテはGitHub等のリポジトリを引き継ぐ + CPANモジュールのcomaintに登録してもらうことで引き継ぐことができます。

gfx.hatenadiary.org

この部分はオリジナルの作者の方にやっていただく必要があるため、コミュニケーションが必要です。 権限を付与されてからは自分のCPANモジュールをメンテするように操作することが可能です。

ただし引き継がれたCPANモジュールをアップロードする際はx_authorityという権限に気をつける必要があります。

blog.64p.org

※追記 id:shoichikaji さんより最近はいらないとの情報を得ました!

※追記ここまで

新しいモジュールを作りつつメンテもしていく

新しいモジュールを作るのも楽しいですが、既存のモジュールのメンテをするのも盆栽をするようで楽しいです。 また偉大な先人のコードに自分も関わることができ、広く使われていく体験もできるので、ぜひPerlに興味がある方はCPANモジュールなどに関わっていきましょう!!!

明日のはてなエンジニアアドベントカレンダーは同じチームのid:deflis55さんです! お楽しみに!

zshでPATHが壊れないようにPATHに新しいディレクトリを通す

TL;DR

特に順番は気にしないとき

path+=('/hoo/bar/baz');

最初にいれたいとき

path=('/hoo/bar/baz' $path)

PATH通そうとして壊れるヤツ

UNIXを使っている上で避けて通れないのが環境変数$PATHでしょう。 :区切りにディレクトリを列挙して、列挙されているディレクトリ直下に置かれているバイナリファイルをコマンドとして使えるようにするアレですね。

そんな$PATHに新しいディレクトリを追加しようとして、ついつい次のような事故がよく置きます。

export PATH="/hoo/bar/baz"

こうしてしまうと最初から$PATHに設定していたデータが吹っ飛んで、PATHの中身が/hoo/bar/bazだけになってしまいます。こうなるとlsとかのコマンドが使えなくなる訳ですね。

zshだと$pathで配列として扱える

この問題は何が原因かというと$PATHの中身が単なる文字列なのが結構原因何じゃないかなと思っています。 $PATHは明らかに配列構造なので、配列のインターフェースとして扱えればある程度事故が減るはずです。

zshではなんと$pathという変数があり、機能そのものは$PATHと同様に環境変数PATHなのですが、$pathは配列として設定されています。

これによってzshでは配列に対して+=で末尾に要素を追加できるので、path+=('/bar/baz')とか書いておけば、export PATH="$PATH:/bar/baz"と書いたのと同じ意味になります。最後にPATH書く必要がなくて便利。

とはいえ先頭についかするunshift相当の操作は演算子でできないので、path=('/hoo/bar/baz' $path)みたいに書く必要があります。まぁこれはしょうがない。

という訳でzshPATHを操作するときは$path使ったほうが便利です。どうぞお試しください。

参考

stackoverflow.com

git.ioは非推奨(read only)になってしまったので注意する必要がありそう

表題が全てシリーズです。(シリーズとは)

完全にノーマークだったのですが、GitHubが提供していた短縮URLサービスのgit.ioが4月27日を持ってread onlyに移行しています。

2022-04-27 Update: While the git.io url redirection service is read-only and use of the service is limited, we have received feedback from developers and academic researchers who have published git.io links in print documentation and research papers. In order to preserve the integrity of these historical documents, we have decided to archive the current git.io links in a new read-only service that will allow us to serve redirects for those links longer term.

As we continue our analysis, we may remove individual links that point to spammy, malicious or 404 links. Our goal is to not break links relied on for legitimate use, especially by the academic community, while preserving the security of developers on GitHub.

That said, we still encourage users to make use of one of the many URL shortening services available with greater functionality than the git.io service provided. GitHub support will not be able to update or edit redirection records served by the git.io archive service.

github.blog

もともとは4月29日に全てのリダイレクトを止める方針だったようですが、論文等ですでに利用されていることもあり、リダイレクトは止めず、新たに追加/編集等ができない読み取り専用に移行したようです。

(これはgit.ioがスパムなどの目的で使われてしまっており、そのメンテナンスコストを鑑みての判断らしいです。)

read-onlyになったのでまだ使用できるとはいえ、非推奨になってしまったので、新たなURLに移行するのが良さそうです。

Perlの人むけ情報

Perlの人向け情報としては、cpmcpmを使ってインストールする方法が、以前はgit.ioを使ってインストールする方法が推奨されており、この問題で推奨インストール先のURLが変更されているので注意が必要です。

github.com

これは作者のid:shoichikajiさんからもCHANGELOGで変更が呼びかけられています。

github.com

差分は次のような感じです。

旧版

$curl -fsSL https://git.io/cpm | perl - install -g --with-develop --with-recommends --show-build-log-on-failure

移行版

$curl -fsSL https://raw.githubusercontent.com/skaji/cpm/main/cpm | perl - install -g --with-develop --with-recommends --show-build-log-on-failure

ということで気づきベースで変更していくのが良さそうです。git.ioがこんな状況になってたの知らなかった....。

5.32以降のdocker-perlでのcpanmは`https://www.cpan.org`に繋ぎに行くようになります

2022/06/13 追記

一旦この修正はrevertされたので、現在はhttpsで繋ぎにいかなくなっています

2022/06/03 23:46追記

skajiさんの指摘でPERL_CPANM_OPTを指定した状態だと、cpanmの一部の挙動に制限がかかることがPRに書かれました。 この状態だとmetacpanやcpanmetadbのresolverが使用できなくなるので、今後変更があるかもしれません。

github.com

追記ここまで


表題がすべてですが、 library/perl: Update for cpanm update and HTTPS install by default by zakame · Pull Request #12569 · docker-library/official-images · GitHub のPRがmergeされ次第、5.32以降のdocker-perlでのcpanmはhttps://www.cpan.orgに繋ぎに行くようになります。

これは先日docker-perlでインストールされるcpanmのバージョンを最新にしたところ、docker-perlのメンテナのzakameさんが以前のCPAN脆弱性対応のために乗り気で入れたものです。

具体的に何をしたかというと、環境変数としてPERL_CPANM_OPT"--from https://www.cpan.org"を設定しています。 これはcpanmがデフォルトで接続に行く先をhttps://www.cpan.orgにする設定です。

この変更によって表題のことが行われます。

影響のあるPerlバージョン

5.32以降の安定バージョンです。 正確に言うと5.32.1, 5.34.1, 5.36.0の3バージョンが影響を受けます。  docker-perlのtagの関係上、5.32と指定した場合は5.32.1と同じ意味になりますので、5.32以降のPerlをdocker-perlで使っている方は影響があります。

https://hub.docker.com/_/perl

対して5.30以前のPerl Imageは影響を受けません。 これはdocker-perlの現在の開発方針が、「EOLを迎えていないPerlバージョンのみImageの積極的に更新を行う」方針であるためです。

endoflife.date

(そのため現在でもDebian11(bullseye)ベースのイメージは5.30以前のPerlバージョンはなかったりします。)

以下にこの変更による影響をまとめます。

DockerImage自体

ca-certificatescurlがslimのImageでもインストールされたままになります。

github.com

cpanm

cpanmのバージョンが最新になります。

特に問題なくcpanmでモジュールインストールが可能です。 テストではMojoliciousをインストールしています。

github.com

ciの結果を見る限りしっかりhttpsでインストールされてます。

carton

Cartonに関しては環境変数が別となるのでhttpのままになります。

github.com

いやでもhttpsで接続したいんだが...!? という方はCartonの後継プロジェクトとなっているCarmelの方をお使いください。 CarmelはCartonと同じ機能を持ちながら、デフォルトでhttps://cpan.metacpan.org/に繋ぎに行くので安心です。

github.com

cpm

特に影響はないと思われます。

まとめ

  • 5.32以降のdocker-perlでのcpanmはhttps://www.cpan.orgに繋ぎに行くようになります
  • 同時にcpanmも最新になります
  • docker-perlはEOLを迎えてないPerlのバージョンのみ面倒を見ているので、docker-perlを使う方はバージョンに注意しましょう

現場からは以上です

windowsファイル転送バトル

実家に帰省するなどすると色々あり、例えばwindows8自体のPC(i3, メモリ4GB)を無理やりwindows10にしたかろうじて動いているPCからデータを別のwindowsPCに移動させるなどのミッションが生じる。

ここで外付けHDDやUSBメモリを使わないことを考えると、windows間で自宅ネットワークを活用してファイル転送をしたい。 ということで様々な方法でwindows間のファイル移動を試みた。

windows自体のファイル共有機能を使う -> 失敗

windows ファイル移動 ネットワーク越し」的なのでぐぐるとまず出てくるやつ。

support.microsoft.com

ネットワーク共有設定からファイル探索を許可した上で、共有したいファイルを選択するもの。 しかしこの方法ではwindowsPCが古すぎるのが原因か、微妙に違うSSIDに接続しているのが原因かは定かではないが、転送を受ける側のPCで転送元のPCがネットワーク探索できなかった。ので断念。

新パソコン側からsftpで旧パソコンからデータを回収する => 失敗

ファイル共有機能が使えなかったので、別の方法でネットワーク越しにファイルを転送することを考える。 市販のファイル転送ソフトは金かかるので却下。無料で行きたい。 windowsはデフォルトでrsyncが使えないこともあり、外部アプリを入れずに素手で戦う場合の次の一手はsftpになった。 windowsWinSCPというイカしたGUIアプリがあるのでこれを使うと便利。

forest.watch.impress.co.jp

sftpを使うということはつまりssh接続が必要になるのでsshdを動かす必要がある。 なんと最近のwindows10はアプリストア的なところからsshdをインストールできるので古い方のPCでインストールをしようとしたところ、なぜかGUI経由だと20%でインストールが止まる。そんな。

調べたらPowerShellでインストールする方法があったので試したらこれで行けた。やはりCUIは正義。 docs.microsoft.com

Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

# Start the sshd service
Start-Service sshd

# OPTIONAL but recommended:
Set-Service -Name sshd -StartupType 'Automatic'

# Confirm the Firewall rule is configured. It should be created automatically by setup. Run the following to verify
if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) {
    Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..."
    New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
} else {
    Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists."
}

これでsshdは元気に動き出したのだけど、sshしようとすると同頑張ってもPermissionDeniedになる。そんな。22番ポートは空いてるのに。

ユーザー側の設定がだいぶカオスな事になっていそうなので、ログインユーザーでログインするのを諦めてadministratorでsshできるように仕込んだ。

qiita.com

これでsshできたぞ!!! sftp/scpや!!! と思ったら、なんとadministratorでsshすると、移動したいファイルがsshした先では確認できるのに、sftp/scpすると確認できない(転送されない)という自体が出た。 所有権の問題かと思ってtakwownで所有権を直したのだけど、効果は無く、キレそう。

atmarkit.itmedia.co.jp

ということでこの方法も諦めて、逆の発想で新PCから旧PCにsftpしてファイルを持ってくることにした

旧パソコン側からsftpで新パソコンにデータを送る ==> 成功

新パソコン側でsshdの設定をし、旧パソコンにWinSCPをまずいれる。 このインストールが曲者で、メモリが4GBしかないのでなんか良くわからないアプリが立ち上がるとすぐ固まる。 起動時にその部分さえケアできればあとは新PCに接続し、ファイルを転送すればすんなりクリアだった。

というわけでファイル転送バトルでした。年末という感じですね。良いお年を。