sironekotoroの日記

Perl で楽をしたい

Jest で __tests__ フォルダにテスト用ファイルを置いたのに読み込んでくれない(解決)

タイトルの通り

Jest ってのは JavaScript / TypeScript のテストフレームワークです。

jestjs.io

テスト実施時に hoge.test.ts のように拡張子の手前に test っていう文字列を入れたファイルを用意するか、プロジェクトフォルダの直下に作った __tests__ フォルダの下にテスト用のスクリプトファイルを入れておくと、テストをやってくれます。

くれるはずなんですが・・・うちの環境では __tests__ に入れたファイル読んでくれませんでした。

Jest の設定ファイルである jest.config.js 見てみると、パターンマッチ的にも問題なさそうです。 うーむ。

// The glob patterns Jest uses to detect test files↲
 testMatch: ["**/__tests__/*.+(ts|tsx|js)",↲
   "**/?(*.)+(spec|test).+(ts|tsx|js)",↲
 ]

"**/__tests__/*.+(ts|tsx|js)",↲ ここ。

で、試しにパターンマッチを全部削って ./__tests__/hoge.test.ts と直接ファイル名を記述したりしたけどダメ。

なぜだ。

まぁ、テスト自体は src/ に入れた hoge.test.ts を読み込んでくれるので、テストを実施する回避策はあるんですが、納得できない。

納得は全てに優先します、自分のプロジェクトであればなおさら・・・

解決

今回、TypeScript を書くにあたってソースファイルを src/ フォルダの中に入れていたのですが、そのとき Jest の設定を書き換えていました。

以下の設定をコメントアウトして有効にしていました。

   // A list of paths to directories that Jest should use to search for files in↲
  roots: ["<rootDir>/src",↲
  ],↲

はい、ちゃんと書いてますね。

A list of paths to directories that Jest should use to search for files in↲

Jestがファイルを検索する際に使用するディレクトリのパスのリスト↲

この設定が有効になっていると、Jest はファイルを探すときに src の中しか見ないわけです。

ということで、ここをコメントアウト

無事、テストを __tests__ フォルダに隔離してもテストを実行してくれるようになりました。

めでたしめでたし。

なんで TypeScript 書いてるん?

Google Apps Script (GAS)で動かすためです。

これまで GAS が提供している Web エディタで書いてきたのですが、そろそろ適切な分割とかモジュールの利用をしないと厳しくなってきました。

GAS、特に Sheet と密接に関わるコードは(モックとか作るの面倒なので)継続してウェブ上のエディタで JavaScript 書いていきます。

しかし、Sheet と関係のないところは、切り出して小さいモジュールを作って良いのではないか?

小さいモジュールなら、実験がてら TypeScript で書いてみてもいいのでは?

と思った次第です。

いまは 2021 年 10 月 1 日に変更された PayPay銀行と三菱UFJ銀行の振込手数料判定プログラムなどを書いています。

www.paypay-bank.co.jp

www.bk.mufg.jp

手元で TypeScript を書いて、clasp というツールで GAS に反映させています。

TypeScript はなんというか、コンパイルして JavaScript を吐き出すことが迂遠と思っていたのですが、以下の資料を見て認識を改めた次第です

future-architect.github.io

新しい記法を使うが、ブラウザの互換性を維持するコードを書く手法としてコンパイラを使うのが当たり前になってきた

TypeScript については環境構築や、どのツールを使えばいいのかもわからないというのが正直なところですが、とりあえず Web+DB の TypeScript 特集とかみながら試行錯誤中です。

gihyo.jp

なぜこんな小ネタを?

  • 普段から Perl 入学式で「わからなかったところをブログとかに残しておくと、数年後の自分が面白い」と言ってることもあり、数年後の自分を楽しませるため

  • ググっても同じようなネタで苦しんでる人がいなかったため(StackOverFlow にいたけど、ちゃんとした回答ついてなかった)

Perl のサブルーチンにおける引数の取り込みかたを 3 種類ほど紹介

その 1:おなじみ @_

先に開催された Perl入学式で、サブルーチンに渡す引数について「Perl においては仮引数はない」というようなお話をしました。

仮引数というのは、例えば JavaScript でいうと fizz の横にある number のことです。

function fizz(number) { // この number が仮引数 
  if (number % 3 === 0) {
    return 'Fizz';
  };
}

console.log(fizz(3)); // Fizz
console.log(fizz(5)); // undefined

おなじ関数を Perl で書くとこうです。

仮引数がない代わりに、引数を取り込む処理をサブルーチン内部で my $number = shift @_ として行っています。

sub fizz {
    my $number = shift @_;

    if ( $number % 3 == 0 ) {
        return 'Fizz';
    }
}

print fizz(3) . "\n";   # Fizz
print fizz(4) . "\n";   # 何も表示されない

これがスタンダードな Perl のサブルーチンにおける引数の取り込みかた 1 つ目です。

その 2 :実験的機能 signatures

Perl にはいくつか実験的な機能が搭載されています。

その中の一つに仮引数に関するものがあります。それが signatures です。

実験的機能は通常は無効になっていますが、明示的に記述することで有効にできます。

perldoc.jp

これを使って書くと、だいぶ JavaScript の例に近くなります。

というかほぼ同じ?

use feature 'signatures';   # 実験的機能のうち signatures を使う旨の宣言
no warnings "experimental::signatures";
                            # 実験的機能利用時の警告メッセージを消す

sub buzz ($number) {
    if ( $number % 5 == 0 ) {
        return 'Buzz';
    }
}

print buzz(5) . "\n";   # Buzz

実験的機能とはいえ、この機能が搭載されたのは Perl 5.20 で 2014 年。もう 7 年になります。

最近 Perl を入れたのであれば大体使えるはずなので、個人プロジェクトや、新しい Perl が稼働する環境では使って良いと思います。

なお、Perl のバージョンを確認するにはコンソールから perl -v 、または Perl のプログラム内では print $] で表示されます。

その 3:モジュール Function::Params

Perl には・・・というか、大抵のプログラム言語には外部からプログラムを追加することで、機能を追加する仕組みがあります。

Perl の場合には「モジュール」と呼ばれています。

JavaScript でもモジュールで、 Ruby だと Gemジェム ですかね)

Function::Params モジュールはインストールこそ必要ですが、仮引数的な書き方が可能です。

なお、このモジュールを使う場合にはサブルーチンの宣言は sub ではなく fun になります。

gihyo.jp

use Function::Parameters;
fun fizzbuzz($number) {
    if ( $number % 3 == 0 && $number % 5 == 0 ) {
        return 'FizzBuzz';
    }
}

print fizzbuzz(15) . "\n"; # FizzBuzz

脱線:引数の型チェック

Function::Params は型チェックをする他のモジュールと組み合わせることで、引数の中身が希望の型か確認できます。

use Function::Parameters;
use Type::Tiny;                 # 型モジュール
use Types::Standard qw(Int);    # Int 整数型を宣言
fun fizzbuzz( Int $number) {    # Int 型の引数に限定
    if ( $number % 3 == 0 && $number % 5 == 0 ) {
        return 'FizzBuzz';
    }
}

print fizzbuzz(15) . "\n";    # FizzBuzz
print fizzbuzz('foo') . "\n"; # Int 型ではないのでエラー
                              # In fun fizzbuzz: parameter 1 ($number): Value "foo" did not pass type constraint "Int"

というわけで

Perl のサブルーチンにおける引数の取り込み方でした。

Perl 入学式ではモダンな(現代的な)Perl を教えることを旨としています。

これまでに書かれてきた Perl の書き方については当然学ぶ必要がありますが、よりモダンな書き方も教えていく必要はあるなぁ、と思ったのでした。

Perl入学式 オンライン 2021 第3回お疲れ様でした

Perl入学式 オンライン 2021 第3回

受講された方、サポーターの方、お疲れ様でした。 講師の人です。

今回は配列操作の関数、ハッシュ、ハッシュ操作の関数、サブルーチンを学びました。

今回のスライドの内容です。

github.com

講義中に解いた練習問題の回答例です。

github.com

講義スライドや動画を見て、不明点がある場合には Discord で質問してみてください。第 0 回の環境構築、第 1 回、第 2 回、前年度の内容などについてでも大丈夫です。

サポーター陣一同お待ちしております

discord.com

sort について

講義中、配列の並び替えのところで sort を紹介しました。

my @array  = ( 5, 4, 3, 2, 1 );
my @sorted = sort { $a <=> $b } @array;
print "@sorted";    # 1 2 3 4 5

受講生の方から、この時の $a$b には何が入っているのか?との質問がありました。

こういうのは嬉しいですね。助かります。

実際に講義中に校長である xtetsuji さんからのアドバイスを受けて書いたものを、これまでの講義範囲の内容で書き直すとこんな感じです。

my @array  = ( 5, 4, 3, 2, 1 );
my @sorted = sort {
    print '$a:' . "$a\n";
    print '$b:' . "$b\n";
    print '=========' . "\n";
 $a <=> $b
 } @array;

print "@sorted";    # 1 2 3 4 5

実行結果がこれ

$a:5
$b:4
=========
$a:3
$b:2
=========
$a:4
$b:2
=========
$a:4
$b:3
=========
$a:2
$b:1
=========
1 2 3 4 5

配列の先頭から 2 つとってきて、$a と $b に代入していますね。

代入した後は <=> 宇宙船演算子で比較しています。

講義時には解説しませんでしたが、この宇宙船演算子は、左側が小さい時は -1, 同じ値の時は 0 , 右側が小さい時は 1 を返します。

早速、今回学習したサブルーチンを使ってみてみます。

print "1 <=> 2 : " . spaceship( 1, 2 ) . "\n";
print "1 <=> 1 : " . spaceship( 1, 1 ) . "\n";
print "2 <=> 1 : " . spaceship( 2, 1 ) . "\n";

sub spaceship {
    my ( $a, $b ) = @_;
    return $a <=> $b;
}
1 <=> 2 : -1
1 <=> 1 : 0
2 <=> 1 : 1

この宇宙船演算子の結果により、左右の値の大小を比較しています。

つまり、sort 関数は二つの値を取り、比較し、並べ替えることを繰り返すことで、最終的に配列全体を並べ替えています。

ソートアルゴリズムは奥が深く、アルゴリズムの例題としてよく取り上げられます。

自分は情報処理試験で複数のソートアルゴリズムがあることを知った口ですが、興味ある方は Perl (や得意な言語)で実装してみると面白く、かつ難しさがわかるかもしれません。

ja.wikipedia.org

次回

10 月以降を予定していますが、新型コロナウイルスの感染状況によってはオフライン版との並行になるかもしれません。

次回は Perl 初心者の鬼門「リファレンス」と「正規表現」をやる予定です。

Perl つかって、macOS 上で Windows のパスを処理しようとして10分くらいハマる

たとえば h:\Dropbox\temp.jpg こんな感じのパスから temp.jpg を取り出したいと思いました。

Perl でパス処理、それも異なる OS 環境で!といったら標準モジュールの File::Spec です。

perldoc.jp

実際にはファイルにパスのリストが行区切りで書かれているのですが、簡略化するために直接パスを書いてみます。

最初に書いたのはこういう感じのコードでした。

#!/usr/bin/env perl
use strict;
use warnings;
use File::Spec;

my $path = 'h:\Dropbox\temp.jpg';
my ( $volume, $directories, $file ) =
    File::Spec->splitpath($path);

print $file . "\n";    # h:\Dropbox\temp.jpg

が、パースされずにそのまま出てきてしまいます。

うーん?

試しにMacのファイルでやってみると問題なくパースされます。

#!/usr/bin/env perl
use strict;
use warnings;
use File::Spec;

my $path = '~/Desktop/search.txt';
my ( $volume, $directories, $file ) =
    File::Spec->splitpath($path);

print $file . "\n";    # search.txt

あれー、ちゃんと動いてるなーってことでドキュメント見てみます。

これらの関数はほとんどのオペレーティングシステムによって異なるので、 それぞれの OS 固有のルーチンは次のような別々のモジュールで利用可能です:

        File::Spec::Unix
        File::Spec::Mac
        File::Spec::OS2
        File::Spec::Win32
        File::Spec::VMS

現在の OS のための適切なモジュールは File::Spec によって自動的に 読み込まれます。 (VMS のような) 一部のモジュールはその OS でのみ利用可能な機能を 使っているので、全てのオペレーティングシステムで全てのモジュールが 読み込まれるわけではありません。

なるほど、手元の環境は mac なので、 macOS 用のモジュールが自動的に読み込まれる。そのモジュールで Windows のパス処理をしようとして失敗していたのか。

ってことで、以下のように変更して当初の目的を遂げたのでした。

#!/usr/bin/env perl
use strict;
use warnings;
use File::Spec::Win32;    # Windows のパスを処理するモジュールにした

my $path = 'h:\Dropbox\temp.jpg';
my ( $volume, $directories, $file ) =
    File::Spec::Win32->splitpath($path);

print $file . "\n";    # temp.jpg

思えば、今までは Windows で動かすコードは Windows 上で、Mac 上で動かすコードは Mac で書いてきたので、この問題にぶつからなかったんだなぁ。

本当にやりたいことはまだまだ先なのですが、まぁ、千里の道も一歩から。やっていきましょう。

ノラカン「あの素晴らしいCGIからもう一度」を読んだ

技術書同人誌博覧会

というイベントが 2021 年 6 月に開催されました。

gishohaku.dev

電子書籍でいくつか買い、ダウンロードしたファイルをリネームし、iPad でも開ける Dropbox のフォルダに格納したまま・・・になっておりました。

かつては通勤時間や旅行・帰省の際にこういった本を読んでいました・・・が、コロナ禍による「新しい生活様式」のなかですっかり読むタイミングを逸してしまっていました。

昨日、成分献血という拘束された状況で未読の本があることを思い出して読んだのが「あの素晴らしいCGIからもう一度」です。

booth.pm

わたしとCGI

本格的にプログラミングと Perl を学び始めた 2013 年時点で、既に CGI は化石みたいな扱いであったと思います。

書籍で学ぼうにも情報が古かったり、また環境構築に手間取った覚えがあります。

たしか、 XAMPP 入れて簡単な掲示板やアクセスカウンターを動かした記憶があります。

それ以降は Perl入学式で学んだ Mojolicious::Lite という Web フレームワークをメインに学んできました。

3章 アプリケーションコードの呼び出し

Plack が必要とされる歴史的背景については知りませんでした。

でも、この本の以下の記述でやっと理解できたわけです。

CGI が高速化のためのネイティブ API が利用できないとか、CGI.pm が大きなモジュールであることから忌避され、結果としてフレームワークが出てきて・・・の続きです。

フレームワークごとにウェブアプリケーションの呼び出し方も異なります。簡単な例を考えてみると...

- あるものはリクエストのデータを配列でくれ、といい
- あるものはリクエストのデータはハッシュでくれ

といっているような感じです。

3.4 ちょっとでかすぎたCGI: 効率向上を求めたアプリケーションサーバCGI.pmを避けるように 48p

ここで、あー、だから PSGI によるインターフェースの統一が必要だったんだ、と、やっと理解できたのでした。

確かに、自分で作ったプログラムでも作る時期、日によってサブルーチンに渡す値がハッシュだったりハッシュリファレンスだったりします・・・(目を背けながら

改めてみる「Perl ウェブ開発の中世〜CGIPlack の間〜」

この辺り、じつは 2017 年の YAPC::KANSAI OSAKA で Perl 入学式の現校長 id:xtetsuji さんが発表しています。

この本でも参考文献の一つとなっています。

当時はこれを聞いて、「わかったつもり」になっていましたが、今思うと浅い理解であったなぁ、と思います。

そして、かつてよりわからないことが増えた気がします。

これは無知の知とでも言うか単なる老化なのか、はてさて。

単純に Perl のことが語られていて嬉しい

この本は 1 章丸々割いて PSGI について語ってくれています。

近年は Perl についての本があんまりないだけに、それだけでも嬉しかったですね。

当然、わからない用語や聞いただけの用語・単語もあるのですが、理解していくためのとっかかりとしてとても良かったです。

Perl で重複した値をみつける

連休前の出来事

業務で、1 〜 1500 の数字の中から重複しているものを見つける必要があり、その時に 1 分くらいで書いたのが以下の Perl スクリプトです。

#!/usr/bin/env perl
use strict;
use warnings;

my %hash;
for my $line (<DATA>) {
    chomp $line;
    $hash{$line}++;
}

for my $num ( sort keys %hash ) {
    if ( $hash{$num} > 1 ) {
        print $num , "\n";    # 3 6
    }
}

__DATA__
1
2
3
3
4
5
6
6
7
8
9
# 以下、1500まで

重複している 3 と 6 だけ表示されます。

こんな感じでちゃんと動いて用はたせたんですが、問題は 1 行書くたびに

「絶対に重複してるものを選び出す関数あるだろ」

「たしか、List::Util ってモジュールに uniq っていう重複のない値を出す関数があったから、その逆もあるに違いない」

「ここでググってみるか、いや、このまま書き続けた方がいいか」

「いや grep とか使ってなんとかできるんじゃないのか」

「というか、元のデータ Google Sheet だからそっちでも重複検出する何かあるだろ何か」

という思いが繰り返し去来することでした。

まぁ、時間あれば調べたんですが、こんときはトラブルシュートでとりあえず速度が必要でした。

重複した値を見つける

これ、ブログのネタになるなーと思って連休に突入したのですが、なんか夜更かししたり映画観に行ったりしているうちに最終日になってしまいました。

ここで書かないと忘れるよなぁ、ってことで書きます。

List::MoreUtils の duplicates

Perl の外部モジュールです。cpanm でサクッとインストールしましょう。

$ cpanm List::MoreUtils

使う関数は duplicates 、そのまま重複って意味です

metacpan.org

#!/usr/bin/env perl
use strict;
use warnings;

use List::MoreUtils qw/duplicates/;

my @duplicates_nums = duplicates(<DATA>);
chomp @duplicates_nums;
print "@duplicates_nums";    # 3 6

__DATA__
1
2
3
3
4
5
6
6
7
8
9

・・・こっちで良かったじゃん。duplicates ってちゃんとやりたい名前もついてて、あとから見た人(数日後の自分)にも優しい。

まぁ次回からはこっち使おう・・・

grep つかう

あんまり短くならなかった。

#!/usr/bin/env perl
use strict;
use warnings;

my %hash;
grep {chomp $_;$hash{$_}++} (<DATA>);

for my $num ( sort keys %hash ) {
    if ( $hash{$num} > 1 ) {
        print $num , "\n";
    }
}

__DATA__
1
2
3
3
4
5
6
6
7
8
9

grep の中に全部入れてもいいけど可読性良くない

#!/usr/bin/env perl
use strict;
use warnings;

my %hash;
grep {
    chomp $_;
    $hash{$_}++;
    print "$_\n" if $hash{$_} > 1
} (<DATA>);

__DATA__
1
2
3
3
4
5
6
6
7
8
9

Google Sheet の関数

列、または行から直接重複している値のみを抜き出す関数はなさそう。

シート上部のメニューにある「データ」のなかに「重複の削除」というのはあるけど、重複している値のみを表示するってのはなかった。

だもんで、COUNTIF で重複する値を探してソートって感じ?

query 関数とか使えばいいかな?(やってない(SQL でいうところの HAVING 句がないから難しそう

ってことで

重複した値探す時には List::MoreUtils 使うと楽で良いよね、という話でした。

はてなインターネット文学賞「わたしとインターネット」

はてなインターネット文学賞「わたしとインターネット」

当時大好きだった人が東京から京都に引っ越すことになった。

引っ越す理由の一端はうちにもあり、今思うと本当に申し訳ない気持ちだ。

タイムマシンがあって当時に戻れたら自分を殺しちゃうだろう。

その人と連絡を取る手段がインターネットのメールだった。

草の根BBSのメッセージではなくて、インターネットのメール。

だから、Windows95 が動く IBM の Aptiva を買った。

Aptivaは当時SMAP香取慎吾くんがCMをやっていた。

ifworlddesignguide.com

雑誌の付録の CDROM で、interq つかってインターネットに接続した。

メールアドレスのアカウント名とパスワードは同じ文字列を使った。忘れなくていいと思った。

バカだった。アホだった。

バカでアホなので、更なる(自分の)不義理でその人との縁は切れてしまうのだけど、それでもパソコンとインターネットは残った。

なんやかやで、就職して、退職して、アルバイトして、就職して、プロバイダのサポート窓口についた。

大手プロバイダってことで、接続からホームページ作成からブログのサポートまで色々やった。

それから異動でネットワーク機器のサポート(中身は Linux )して、社内ネットワークのサポートして、転職してちょっとだけプログラムを書き、今は経理をメインでお仕事してる。

あれからずっと、今の経理の仕事さえも、インターネットなくしてはあり得ない世界になった。

いろんな出会いと別れも、出会いと別れの間の接触も、インターネット経由での方が多くなったかもしれない。

インターネットが自分から剥がれる時が来るのだろうか?

攻殻機動隊のように、死の直前、死のその時、死を超えて、自分をアップロードする時がくるんだろうか。

インターネットから自由でありたいな、と思いつつ、今日もインターネットにつながっている。