sironekotoroの日記

Perl で楽をしたい

PerlでWebから情報を持ってくるときのツール

まず注意

昨今のWebではhttps化が進んでいます。https の s はSecureの s です。安全な通信をするためにデータを暗号化/復号するのですが、そのためのモジュールが別途必要です。

運が良ければ、すでにインストールされているかも・・・

Perl入学式で構築した手元のMsys32(Windows7上に構築)だと

  • のちに出てくる HTTP::Tiny のサンプルスクリプトを試したところ、httpのサイト(例:http://www.hatena.ne.jp/)はアクセスできるが、httpsのサイト(例:https://www.yahoo.co.jp)にPerlスクリプトからアクセスできず。

  • エラーメッセージによると、 Net::SSLeay が無いとのこと。

  • cpanm Net::SSLeay

    • インストール失敗
    • エラーメッセージによると、MSYS側にSSL環境がなさそう
  • pacman -Syu

    • MSYS側の更新がなくなるまで複数回実行&再起動
    • エラー変わらず
  • pacman -S openssl

    • f:id:sironekotoro:20190728112219p:plain
    • MSYSのopensslのインストールはできたが、 cpanm Net::SSLeayは失敗
  • pacman -S perl-Net-SSLeay

    • cpanm じゃなくて、MSYSのパッケージマネージャ(pacman)で入れてみることにする
    • f:id:sironekotoro:20190728114636p:plain
    • インストールはされたが、httpsのサイトへのアクセスは失敗する・・・が、エラーメッセージが変わった!一歩前進!
    • こんどは IO::Socket::SSLがないだとー
  • cpanm IO::Socket::SSL

    • 大文字小文字を厳密に見てくるので注意
    • こっちはなんか時間切れとか出てくる。きー!
  • pacman -S perl-IO-Socket-SSL

    • インストールできた
  • うごいた!

こんな感じでした。ここまでが長い。でも、動いてしまえば「許そう、全てを・・・」って気分になりませんか。うちはなります。

HTTPクライアント編

HTTPクライアントというのは、Chromeとか、Firefoxとかそういったブラウザです。HTTPサーバーにアクセスし、情報を得るものです。

プログラムからもHTTPサーバにアクセスして情報を得ることができます。

以下はPerlでのHTTPクライアントの紹介です。

HTML::Tiny

perldoc.jp

Perl 5.13.9以降であればコアモジュールとしてPerl本体と一緒にインストールされるモジュール。なので、普通にPerlをインストールすれば大概の環境では使えるはず。

$ corelist HTTP::Tiny

Data for 2018-11-29
HTTP::Tiny was first released with perl v5.13.9

HTTP::Tiny のページにあるサンプルをPerl入学式にある範囲で書き直したものです。あと、対象を日本のYahoo Japanに変えています。

$response->{content} とか、リファレンス回をやった人だと見覚えがあるのではないでしょうか。ハッシュリファレンスですね。

use strict;
use warnings;
use HTTP::Tiny;

# Yahooのurlを変数に入れる
my $yahoo_url = 'http://www.yahoo.co.jp';

# HTTP::TinyでYahooにアクセスして、getでデータを持ってくる
my $response = HTTP::Tiny->new->get($yahoo_url);

# データを持ってくるのに成功したかどうかを判別
if ( $response->{success} ) {
    # 成功してたら、HTTPのステータスコードを表示。200ならok
    print "$response->{status} $response->{reason}\n";
}
# 失敗してたら、プログラム終了
else {
    print "Failed!\n";
    exit();
}

print $response->{content}; # ヤフーのHTMLが表示される

LWP::Simple

perldoc.jp

ここからは cpanm でのインストールが必要なモジュール。LWP というのは ibwww-perl の略称で、PerlをつかってWebにアクセスするときの基本的なモジュールです。その機能最小版です。

use strict;
use warnings;
use LWP::Simple;

# Yahooのurlを変数に入れる
my $yahoo_url = 'http://www.yahoo.co.jp';

# getでデータを持ってくる
my $content = get($yahoo_url);

# データ持ってくるのに成功したら表示する
if ($content) {
    print $content;    # ヤフーのHTMLが表示される
}

HTTP::Tinyと大体同じですね。うちのコメントも早くも簡素になってきました。

LWP

perldoc.jp

上記 LWP::Simple の大元である、HTTPアクセスに必要なことの全てが詰まったフルセット版です。 ユーザーエージェントの変更やPOSTでのデータ送信など、HTTP通信を使った機能が詰まっています。

use strict;
use warnings;
use LWP::UserAgent;

# Yahooのurlを変数に入れる
my $yahoo_url = 'http://www.yahoo.co.jp';

# LWP::UserAgentのインスタンスを生成する
my $ua  = LWP::UserAgent->new();
# HTTP::Requestのインスタンスを生成するついでに、GETでアクセスしに行くページも設定する
my $req = HTTP::Request->new( GET => $yahoo_url );

# uaがGETでyahooにデータを取りに行く
my $res = $ua->request($req);

# ページの取得に成功してたら内容を表示する
if ( $res->is_success ) {
    print $res->content;  # ヤフーのHTMLが表示される
}

Furl

perldoc.jp

tokuhiromさん作成の「早い」ライブラリ。

Furlはもう一つのHTTPクライアントライブラリです。LWPはPerl5のデファクトスタンダードな HTTPクライアントですが、クリティカルなジョブでは遅すぎますし、週末のハッキングには 複雑過ぎます。Furlはこれらの問題を解決します。楽しんで下さい!

use strict;
use warnings;
use Furl;

# Yahooのurlを変数に入れる
my $yahoo_url = 'http://www.yahoo.co.jp';

# Furlのインスタンスを生成するついでに、GETでアクセスしに行くページも設定する
my $res = Furl->new->get($yahoo_url);

# ページの取得に成功してたら内容を表示する
if ( $res->is_success ) {
    print $res->content;
}

Webスクレイピング

ここまで紹介したものは、Webサーバーにアクセスして、そこから情報を取得するものでした。しかし、ほとんどの用途では、Webの情報が全て必要なわけではなく、ごく一部のみ必要だったりします。

そういった情報を都度、正規表現などを使って取り出してもいいですが、もっと楽はできないものでしょうか?そんなプログラマの三大美徳「怠惰」を実現させるツール、ありそうですよね。

Webサーバから返ってきたデータのうち、必要な部分だけ取り出す、こそげとる、それがスクレイピングと言われるツールです。

ここからはPerlスクレイピングモジュールを使ってYahooニュースからトピック一覧だけ取り出してみます。

Web::Scraper

metacpan.org

うちはこのツールをPerl CPANモジュールガイド という本で知って、ほぼこれをメインに使っています。要素の指定にXPathCSSセレクタを利用することができます。

この本には他にもPerlのモジュールがたくさん載っているので、やや古いとはいえオススメです。

use strict;
use warnings;

# 日本語をターミナルで表示(出力)するときに警告が出ないよう書いておく
binmode STDOUT, ':encoding(UTF-8)';

use Web::Scraper;
use URI;

my $yahoo_news_url = 'https://news.yahoo.co.jp/topics';

my $uri = URI->new($yahoo_news_url);

# どの要素を取得するかを設定する変数
my $scraper = scraper {

    # タイトルを取得するスクレイパー
    # Xpath形式で指定
    process '/html/head/title', 'page_title' => 'TEXT';

    # ニュースタイトル一覧を取得するスクレイパー
    # CSSセレクタ形式で指定
    process 'li.topicsListItem > a ', 'news_titles[]' => 'TEXT';
};

# ここでスクレイピングが実行される
my $res = $scraper->scrape($uri);

# ここから結果表示
print $res->{page_title} . "\n";    # タイトルの表示

for my $title ( @{ $res->{news_titles} } ) {
    print $title . "\n";    # ニュースのタイトルの表示
}

Web::Query

metacpan.org

Web::Query is a yet another scraping framework, have a jQuery like interface.

Javascriptのライブラリ、JQueryに似た方法で要素を指定してスクレイピンすることができるモジュールです。実はJQuery使ったことないので、わからない・・・

でも、直感的に要素を指定できる感じで良いですね。

use strict;
use warnings;

# 日本語をターミナルで表示(出力)するときに警告が出ないよう書いておく
binmode STDOUT, ':encoding(UTF-8)';

use Web::Query;

my $yahoo_news_url = 'https://news.yahoo.co.jp/topics';

# topicsListItem 属性をもつ li タグの下にある a タグを持ってきて、
# a タグのテキスト要素を表示する
wq($yahoo_news_url)->find('li.topicsListItem > a')->each(
    sub {
        print $_->text . "\n";
    }
);

HTML::TreeBuilder

metacpan.org

スクレイピングツールのくくりに入れましたが、HTML::TreeBuilderはHTMLをパース(解析)するためのツールです。

正確なHTMLは<html>の中に<head><body>タグがあり、その中にさらに複数のタグが配置されています。

これを、<html>という太い幹から<head><body>という枝が生え、さらにその枝から別の枝が生え・・・という木構造で解析していきます。

本来は「特定の枝の先にだけ、何らかの処理を行う」ということをしたかったのですが、ちょっと今の自分ではわかりませんでしたわ・・・

use strict;
use warnings;

# 日本語をターミナルで表示(出力)するときに警告が出ないよう書いておく
binmode STDOUT, ':encoding(UTF-8)';

use HTML::TreeBuilder;

my $yahoo_url = 'https://news.yahoo.co.jp/topics';

# new_from_urlメソッドは裏でLWP::UserAgent使っているので、
# LWP入れてないと動かない
my $tree = HTML::TreeBuilder->new_from_url($yahoo_url);
$tree->eof();

foreach $a ( $tree->find("a") ) {

    # news の a タグには data-ual-gotocontent って属性が付与されてた
    # ところで、ual って何だろう・・・urlならわかるんだけど
    my $data_ual_gotocontent = $a->attr('data-ual-gotocontent') || next;

    if ( $data_ual_gotocontent eq 'true' ) {
        print $a->as_text . "\n";
    }
}

閑話休題

WWW::Mechanize

perldoc.jp

一部の人には刺さるかもしれません。私も刺さった人です。

今まで紹介したものは、Webサーバから情報を持ってくるものと、Webページから特定のデータを抜き出す(スクレイピングする)ものでした。

ここで紹介する WWW::Mechanize 、通称Mechメック は、Webサーバからデータを受け取り、そのあとにリンクをたどったり、入力欄に文字を入れてボタンを押す、なんてことができます。

以下は、Yahooの検索ページの入力欄に「Perl入学式」という文字を入力して、その検索結果を得るスクリプトです。

use strict;
use warnings;
use utf8;

# 日本語をターミナルで表示(出力)するときに警告が出ないよう書いておく
binmode STDOUT, ':encoding(UTF-8)';

use WWW::Mechanize;

# Yahooの検索ページのURL
my $yahoo_search_url = 'https://search.yahoo.co.jp/';

# WWW::Mechanizeのインスタンスを生成する
my $mech = WWW::Mechanize->new();

# Yahooの検索ページのHTMLをGETで取得する
$mech->get($yahoo_search_url);

# 取得したHTMLを解析し、フォームの入力欄に文字を入れて、
# そのフォームのボタン(検索ボタン)を押す
$mech->submit_form(
    # 最初のフォーム
    form_number => 0,

    # name属性が p の入力欄に 検索対象の文字を入れる
    fields => { p => 'Perl入学式' },
);

# 結果を表示する
print $mech->{content};

どうでしょう、ずらっとHTMLが表示されて、その中にかすかに検索結果らしきものが見えますね・・・これを整形したい・・・と思いませんか?

うちは思います。

ということで、ここで、先に登場したスクレイピングツールを使います。WWW::MechanizeでYahoo検索した結果から、タイトルとリンクをWeb::Scraperで取り出してみます。

use strict;
use warnings;
use utf8;

# 日本語をターミナルで表示(出力)するときに警告が出ないよう書いておく
binmode STDOUT, ':encoding(UTF-8)';

use WWW::Mechanize;

my $yahoo_search_url = 'https://search.yahoo.co.jp/';

my $mech = WWW::Mechanize->new();

$mech->get($yahoo_search_url);

$mech->submit_form(
    form_number => 0,
    fields      => { p => 'Perl入学式' },
);

my $mech_result = $mech->{content};

# -----ここまでが WWW::Mechanize の部分

# -----ここからは Web::Scraper の部分

use Web::Scraper;

my $scraper = scraper {

# 検索結果を取得するスクレイパーを用意しているところ
# Xpath形式で指定 //と書くことで前方の要素を省略する
# ここでは、
# id が web 要素のdivタグを撮ってきて、その下(直下とは限らない)にある liタグを持ってくる
# という内容
    process '//div[@id="web"]//li', 'search_result[]' => scraper {

    # さらに、その取得条件からタイトルとリンクを分けて取得し、
    # ハッシュで格納する
        process '//a', 'title' => 'TEXT';
        process '//a', 'url'   => '@href';
    };

};

# ここでスクレイピングが実行される
# スクレイプ対象にはmechで検索した後のページを入れる
my $res = $scraper->scrape( $mech_result );

# ここから結果表示
for my $result ( @{ $res->{search_result} } ) {
    print '-------' . "\n";
    print $result->{title} . "\n";
    print $result->{url} . "\n";
    print '-------' . "\n";
}

Web API

さっきので終わらせようと思ったんだけど、ふと、昨今のWebからのデータ取得をいうならWeb APIは外せないのでは?と思い至りました。遅い。

Web APIは簡単にいうと、プログラムが使いやすいように整形されたデータのやり取り、というところでしょうか。詳細はググったり調べたりでお願いします。

もちろん、上記のHTTP::TinyとJSON::PPモジュールを利用することでWeb APIで提供されているデータの取得と利用が容易になります。どちらもPerlのコアモジュールです(5.13.9 以降)。

ここでは、無料で利用できるAPIとして、Livedoorの天気予報APIを利用します。

weather.livedoor.com

地域は東京地方、今日(2019年8月4日)の最高気温を調べてみます。

HTTP::Tiny + JSON::PP

use strict;
use warnings;
use HTTP::Tiny;

# Livedoor天気APIのURLを変数に入れる
my $livedoor_weather_url = 'http://weather.livedoor.com/forecast/webservice/json/v1?city=130010';

# HTTP::TinyでAPIにアクセスして、getでデータを持ってくる
my $response = HTTP::Tiny->new->get($livedoor_weather_url);

# データを持ってくるのに成功したかどうかを判別
if ( $response->{success} ) {
    # 成功してたら、HTTPのステータスコードを表示。200ならok
    print "$response->{status} $response->{reason}\n";
}
# 失敗してたら、プログラム終了
else {
    print "Failed!\n";
    exit();
}

# 取得したデータはJSON形式なので、Perlで操作しやすいように変換する。

use JSON::PP;

my $decoded_response = decode_json($response->{content});

# どんなデータ構造になっているかわからなくなったら、Data::Dumperを使おう!
# use Data::Dumper;
# print Dumper $decoded_response;

# 最高気温
print $decoded_response->{forecasts}->[0]->{temperature}->{max}->{celsius}; # 34

34度!ヤダもう・・・

ところで、my $livedoor_weather_url = 'http://weather.livedoor.com/forecast/webservice/json/v1?city=130010'; の末尾の数字を 270000 に変えることで、大阪の最高気温を出したりすることができます。

ここを変数にすると、他の地域の気温を見たいときに楽ですね。

さらに、その数字を標準入力から入れられると楽かも。

さらにさらに、Webアプリから地域を選べるようにすれば・・・となります。

gist.github.com

そんな、Webアプリからの入出力を学べる Perl入学式 2019 第5回 in東京 お待ちしております!

perl-entrance-tokyo.connpass.com

ojo

Perl入学式 in沖縄の id:AnaTofuZ さんから教えてもらったのがこのツール。

metacpan.org

cpanm ojo でインストールして、コマンドラインから1行、ワンライナーです。

$ perl -Mojo -E'my $content = j g("http://weather.livedoor.com/forecast/webservice/json/v1?city=130010")->{content}->{asset}->{content};say $content->{forecasts}->[0]->{temperature}->{max}->{celsius}'

ということで

PerlでWebにアクセスしてデータ集める方法でした。

Perl入学式 in 東京第4回でピザ会でのお題「双六」

Perl入学式 in 東京第4回でピザ会でのお題「双六」

今回の id:xtetsuji さんからのお題は双六で「あがり」までの平均何回サイコロを振れば良いか?を計算するものでした。 問題のレギュレーションは既にサポーターの id:nishiru3 さんが書いてくれております。 @nishiru3++

nishiru3.hatenablog.com

うちはこんな感じで解いてみました。平均6.3回振ればゴールにたどり着けそう、って感じですかね・・・

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

my $number_of_trials      = 1000000;    # 試行回数。
                                        # 100回とか10万回とか変えられるよう、変数に入れておく。
my $number_of_dice_rolled = 0;          # サイコロを振った合計回数を格納する変数。


for ( 1 .. $number_of_trials ) {        # 試行回数分繰り返す

    my $sum = 0;             # 1試行あたりのサイコロの目の合計を初期化
    while ( $sum < 10 ) {    # 目の合計(双六のマスの位置)が10以下の時は繰り返す
        $number_of_dice_rolled++;   # サイコロを振った回数 +1 する
        $sum += int( rand(6) + 1 ); # サイコロを振る(1〜6の目が出る)

        # サイコロの目の合計(双六のマスの位置)が3の倍数、かつ、
        # サイコロの目の合計が10未満の時は「振り出しに戻る」
        if ( $sum % 3 == 0 && $sum < 10 ) {
            $sum = 0;
        }
    }
}

print $number_of_dice_rolled / $number_of_trials;

今回のピザ会は盛況だった

他にも

とネタが多くて、一通り書いてからブログ公開だとしばらくかかりそうだなーってことで、先にかいた双六から公開しまする。

Perl入学式 2019 in東京 第4回 お疲れ様でした

受講された方、サポーターの方、大変湿度の高い中、お疲れ様でした。
講師をやったジャージの人です。

Perl入学式第4回です。カリキュラム改定に伴い、Perl初学者には難しい(しかし避けては通れない)「リファレンス」で1回丸々使う回となりました。

講義に利用したスライドはMarkdown形式で公開しています。復習に使ってください。

github.com

また、講義中に数回言及しましたが、練習問題も骨のあるものになっています。ぜひチャレンジしてみてください!

github.com

問題の意味がわからない、とか、このような解答例はどうだろう?という方はSlackのPerl入学式チャンネル(招待フォーム)やtwitterハッシュタグ #Perl入学式 をつけて聞いてみてください。
応答速度、監視頻度などの面からSlackの方をお勧めします。

リファレンス!

過去のエントリで大体語り尽くしてる感があるので、そちらで・・・

sironekotoro.hateblo.jp

sironekotoro.hateblo.jp

docs.google.com

講義の最後にもオススメしましたが、Perlのリファレンスに関しては、完全な自信をもって深沢千尋さんの「すぐわかる オブジェクト指向 Perl」をお勧めします。

ピザ会

今日はネタが多くてちょっと後日にまとめて書く予定です。

  • いつもの id:xtetsuji さん出題、サイコロゲームの試行回数

  • Perlライフゲーム

  • Perlでお絵かき Imger

  • Webサイトの死活監視を並列で処理したい!AnyEvent

解決したいコード、困っているコードをプロジェクターに映し出して、いろいろとみていくの、楽しかったですね。モブプログラミングってあんな感じなのかな。

次回予告

次回は8月の後半での開催を予定しています。やるのはWebフレームワーク、Mojoliciousモジョリシャスです。PerlとMojoliciousを通して、Webアプリケーションの骨組みとデータの流れを学習していきます。

やっとこさSledgeが動いた

Sledgeを知っていますか?

Sledgeスレッジというのは、PerlのWebアプリケーションフレームワークです。

2003年にオープンソースとして公開されました。公式ページのurl(http://sl.edge.jp/)に残るように、開発したエッジ(後のLivedoor)の名を冠したプロダクトです。

公開から15年近く経っていますが、Webサービスのバックエンドとしてインターネットの片隅で稼働しており、うちもここ数ヶ月業務で触れているプロダクトです。

Sledge関連情報

インストールしようとググると、Apache1.3とmodperlでの構築を前提としている記事にあたります。

そして現在、Apache1.3とmodperlは現行のOSにインストールする方が難しい・・・ただし、一度動いてしまえば、これらの記事が大いに参考になります。

特にお世話になっている記事を紹介します。

techblog.karupas.org

blog.livedoor.jp

www.skyarc.co.jp

そして、貴重な活字での情報です。vol.1 で終わってしまっているのが悲しい・・・

Sledgeをインストールする

環境

macos 10.14 で試しています。

~/Desktop $ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.14.5
BuildVersion:   18F132

Perlは 5.28 を利用。

~/Desktop $ perl -v

This is perl 5, version 28, subversion 0 (v5.28.0) built for darwin-2level
(with 1 registered patch, see perl -V for more detail)

Copyright 1987-2018, Larry Wall
(中略)

Sledgeの立ち上げにはPlackを使います。 Plackのバージョンは 1.0047

~/Desktop $ perl -MPlack -le'print $Plack::VERSION'
1.0047
~/Desktop $ plenv -v
plenv 2.2.0

インストール

ソースをGithubからダウンロード

tokuhiromさんのPSGI対応版を使います。

~/Desktop $ git clone git@github.com:tokuhirom/Sledge.git
Cloning into 'Sledge'...
remote: Enumerating objects: 174, done.
remote: Total 174 (delta 0), reused 0 (delta 0), pack-reused 174
Receiving objects: 100% (174/174), 79.06 KiB | 487.00 KiB/s, done.
Resolving deltas: 100% (19/19), done.

作成したローカルリポジトリの中に入り、リモートブランチを確認します。

~/Desktop $ cd Sledge/
~/Desktop/Sledge (master) $ git branch --remote
  origin/HEAD -> origin/master
  origin/feature-psgi
  origin/master

origin/feature-psgi に切り替えます

~/Desktop/Sledge (master) $ git checkout -b future-psgi origin/feature-psgi
Branch 'future-psgi' set up to track remote branch 'feature-psgi' from 'origin'.
Switched to a new branch 'future-psgi'

ローカルにcloneしたリポジトリの中に eg/hello.psgi というファイルがあるので、これを一つ上の階層にコピーします。

~/Desktop/Sledge (future-psgi) $ cp eg/hello.psgi .

コピーしたファイルを編集します。

~/Desktop/Sledge (future-psgi) $ vi hello.psgi

3行目に use lib qw(lib/) を追加します。

use strict;
use warnings;
use lib qw(lib/);

plackupで立ち上げます

~/Desktop/Sledge (future-psgi) $ plackup hello.psgi
HTTP::Server::PSGI: Accepting connections at http://0:5000/

ブラウザで http://localhost:5000 にアクセスして、てすと と出てくればokです。 これはt/templateの中にあるunicode.htmlってのを読み込んで表示しています。

ここまで超苦労した

これ、上に書いてある手順だけだと簡単に思えるかもしれないけど、ここまで持ってくるの、めっちゃ大変だったんだからー

まぁ、主な理由はこの hello.psgi を見つけられなくて、会社にあるSledgeシンプル版を参考にして色々と遠回りしてたのが原因です。

このtokuhiromさんのPSGI対応版が独習用に良いんじゃないかな、というのはPerl入学式の2月の合宿でカルパさんやわいとんさんから聞いてはいたけど、そっから思いっきり回り道して、cartonでモジュールインストールしてみたり、会社で使っているやつを参考にして編集したりして、動かない日々が続いてたわけです。

で、なんとか自前で作ったページ(hello.psgiとは別)が動いて、やったー!って思ったらもっとシンプルな実装が元々あったというね。

とりあえず、これで動きました、という例を残しておきます。

Perl入学式 2019 in東京 第3回 お疲れ様でした

受講された方、サポーターの方、大雨の中お疲れ様でした。
講師をやったジャージの人です。

Perl入学式第3回となります。今回はカリキュラム改定に伴う進捗調整のため、前半を第1回、第2回の復習にあて、後半で正規表現を学びました。

講義に利用したスライドはMarkdown形式で公開しています。復習に使ってください。

github.com

問題の意味がわからない、とか、このような解答例はどうだろう?という方はSlackのPerl入学式チャンネル(招待フォーム)やtwitterハッシュタグ #Perl入学式 をつけて聞いてみてください。
応答速度、監視頻度などの面からSlackの方をお勧めします。

講義中のネタについて

Perlcon 2019

東欧にあるラトビアの首都・リガでPerlCon2019が開催されます。

Perl入学式の初代校長のpapixさん、Perl入学式 沖縄のAnaTofuZさんが登壇します。すごい!

perlcon.eu

perlcon.eu

講義中に使ったスライド

自作のスライドです。

特に、最後のページにある EverythingWindowsマシン使うなら必須のソフトだと思っています。CUIはまだ抵抗がある、という方にも優しいGUIのソフトです。ぜひ使ってみてください。

docs.google.com

お仕事での正規表現

最近の仕事で使った正規表現はこんな感じです。

# qwショートカットを利用
my @urls = qw(
    http://perl.example.com/sironekotoro.png
    http://perl.example.com/sironekotoro.html
    http://perl.example.com/cat.jpg
    http://perl.example.com/cat.doc
    http://perl.example.com/kuroneko.gif
    http://perl.example.com/kuroneko.js
);

for my $url (@urls) {
    if ( $url =~ /^http:.*[gif|jpg|png]$/ ) {
        $url =~ s/^http/https/;
    }
    print $url . "\n";
}

ピザ会のお題

Perl入学式 in東京 では講義終了後、希望者で集まりピザ会と称した懇親会を行っています。
(ジュース・ピザ代は実費)
昨年からそこで余興として xtetsuji さんが課題を出し、そこにチャレンジャーが回答して公開する、というようなことをやってます。

xtetsujiさんからのお題ですが、今回は「0〜99までの数字を漢数字を受け取ってアラビア数字にする」でした。

問題のレギュレーションはそのうちxtetsujiさんから公開されるはずですが、入力として与えられる文字列は以下です。

一 二 十 十一 三四 二十一 三十四 三十 三〇

これらの文字列を

1 2 10 11 34 21 34 30 30

に変換するスクリプトを作成します。

うちの回答は「ハッシュテーブルを使ったもの」「正規表現を使ったもの」の折衷型だったので、これを分離して2つのスクリプトに分けて掲載します。いずれも第3回までで習った内容のみで書いています。

あわせて、Perl入学式での範囲にない関数や正規表現を使ったものを「漢数字をアラビア数字にする(Perl入学式でやっていないところ)」として追加で載せておきます。

次回のPerl入学式

次回のPerl入学式 in東京は7月の開催を予定しています。

Perlを学ぶ初心者の鬼門、リファレンスをやります!時間があればHTML入学式もやるかもしれません・・・

皆さんの受講をお待ちしております!

Perl入学式 2019 in東京 第2回 お疲れ様でした

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

カリキュラムを改版して臨んだ第2回となります。

講義に利用したスライドはMarkdown形式で公開しています。復習に使ってください。

www.perl-entrance.org

また、復習問題を用意しています。
第1回と第2回でやった内容のみで解ける問題となっています。ぜひ挑戦してみてください。
復習問題の解答例もありますので、参考にしてください。

問題の意味がわからない、とか、このような解答例はどうだろう?という方はSlackのPerl入学式チャンネル(招待フォーム)やtwitterハッシュタグ #Perl入学式 をつけて聞いてみてください。
応答速度、監視頻度などの面からSlackの方をお勧めします。

ピザ会のお題

Perl入学式 in東京 では講義終了後、希望者で集まりピザ会と称した懇親会を行っています。
(ジュース・ピザ代は実費)
昨年からそこで余興として xtetsuji さんが課題を出し、そこにチャレンジャーが回答して公開する、というようなことをやってます。

xtetsujiさんからのお題ですが、今回は「正弦波を描く」でした。

三角関数、昨年冬あたりに高校数学の復習でやっていたので

  • 正弦波: sin(サイン)を使う
  • 最大は 1
  • 最少は -1

というところまでは知っていたのですが、それをコードに起こすロジックがわからない・・・(5分くらいは考えた)

しかし、それでも何とか、それっぽいのを・・・と頑張った結果がこれです!それっぽい何かが出力されます。

xtetsujiさん曰く「似ている、近似・・・というよりも禁じ手ですね」

bitbucket.org

こんなの世に出していいんだろうか。ってまぁ、こういうのこそ晒していきましょう。晒しただけ強くなれます(強さとは

コードにコメントも書きましたが、Perl入学式の第1回, 第2回の内容で書いています。

リベンジマッチ正弦波を描く・実況中継

そもそも、昨年冬に俺は三角関数の復習をしたんじゃなかったのか?という悔しさがあります。
俺がやったのは問題の解き方だけなのか?という反省もあります。
ということでリベンジマッチです。

まず、Perlのsin関数とは、というところを調べます。

perldoc.jp

sin EXPR
sin

(ラジアンで示した) EXPR の正弦を返します。 EXPR が省略されたときには、$_ の正弦を返します。

ふむふむ。ラジアンが何かはともかく、数学用語だから数字入れてみればいいのかな?

print sin(1) . "\n"   # 0.841470984807897

やはり数字が返ってきた。でも、ラジアンが何者かを理解しないとsin関数はうまく使えないみたい。ではラジアンとは?

(ここで「ラジアン 中学生」「ラジアン 高校」「ラジアン わからない」「ラジアンとは」などでググる

ほーほー、三角関数を求める時の角度ではなく、円の半径と弧の長さから導き出されるものなのね。しかも、角度とラジアンの変換公式もある。

となると、こういう戦略はどうだろう

  1. $kakudo って変数を用意する。初期値は0
  2. whileループを用意・・・いや、無限ループが怖いからfor文にしておこう。最初は20回くらい処理を繰り返してみる
  3. ループ処理の中では、$kakudoを +30 していく
  4. $kakudoは360を超えても気にしない。2週目に入ったという感じで。スノーボードの720ターンみたいな(2回転するターン)
my $kakudo = 0;
for ( 1 .. 20 ) {
    print $kakudo . "\n";
    $kakudo = $kakudo + 30;
}

# 出力
# 0
# 30
# 60
# 90
# 120
# ...
# 570

よさそう。では、この角度をラジアンにするロジックを考える・・・その前に、Perlにそういう関数がないかな?って調べてみる。

Perl ラジアン」でググる

tutorial.perlzemi.com

やっぱりあった。しかもタイトルが既にうちの求めてるものそのまま。おなじみ、木本先生のサイト。安心して参考にさせてもらう。

use Math::Trig 'pi', 'deg2rad';

my $kakudo = 0;
for ( 1 .. 20 ) {
    print '角度:' . $kakudo . "\n";
    print 'ラジアン:' . deg2rad($kakudo) . "\n";

    $kakudo = $kakudo + 30;
}

# 出力
# 角度:0
# ラジアン:0
# 角度:30
# ラジアン:0.523598775598299
# 角度:60
# ラジアン:1.0471975511966
# ...
# 角度:570
# ラジアン:3.66519142918809

うんうん、よさそう。   でも、この変数 $kakudo わかりやすいけど、角度とラジアンの変換関数に揃える形で変数名を変えておこう。
あと、このラジアンの値をsin関数に与えるから、それ用の変数も用意しておこう。

use Math::Trig 'pi', 'deg2rad';

my $deg = 0;

for ( 1 .. 20 ) {
    # ラジアン
    my $rad = deg2rad($deg);
    # サイン
    my $sin = sin($rad);

    print 'サイン:' . $sin . "\n";
    print '-' x 10 . "\n";

    $deg = $deg + 30;

# 出力
# サイン:0
# サイン:0.5
# サイン:0.866025403784439
# サイン:1
# サイン:0.866025403784439
# サイン:0.5
# サイン:1.22464679914735e-16
# サイン:-0.5
# サイン:-0.866025403784438
# サイン:-1
# サイン:-0.866025403784439
# サイン:-0.5
# サイン:0
# ...
}

ちゃんと増えて、減って、増えて、を繰り返してる。けど、値が小さすぎて * で表示した時に困りそう。
あと、マイナスの値の時はどうしよう?

sinの最大値は1 , 最小値は -1 だから・・・1を足せば、0から2の間の値をとるようになるはず。
値が小さすぎる問題は、単純にsinの値を10倍くらいにしてみる。
sinって変数に足したり引いたりしちゃうと本来の意味が変わってしまうから、ちゃんと * の数を格納する変数 $star を新規に用意しよう。

use Math::Trig 'pi', 'deg2rad';

my $deg = 0;

for ( 1 .. 20 ) {
    # ラジアン
    my $rad = deg2rad($deg);
    # サイン
    my $sin = sin($rad);

    # sin + 1 を加えて正の値にしたものを
    # グラフを構成する * の数とする
    my $star = $sin + 1;

    # 最大値2,最小値0ではグラフに華がないので、
    # 10倍の値にする
    $star *= 10;

    # 小数点は邪魔なので、int関数で小数点を切り捨てた
    # 値に丸める
    $star = int($star);

    print 'グラフの * の数:' . $star . "\n";

    $deg = $deg + 30;

# 出力
# グラフの * の数:10
# グラフの * の数:15
# グラフの * の数:18
# グラフの * の数:20
# グラフの * の数:18
# グラフの * の数:15
# グラフの * の数:10
# グラフの * の数:4
# グラフの * の数:1
# グラフの * の数:0
# グラフの * の数:1
# グラフの * の数:4
# グラフの * の数:10
# グラフの * の数:15
# グラフの * の数:18

}

ちょっと数字の増減のペースが気になるけど、行けそうな気がする。
* を数字分だけ表示してグラフっぽくしてみる。
さらに、ループの数も増やして1000回くらいやってみる。 0.5秒ごとにループが回るように、高精度のsleepもいれちゃおう。 このあたりは昨日スクリーンで見た xtetsuji さんの模範解答を参考に。

そして完成したコードがこちら。

bitbucket.org

ということで、うちの回答リベンジ編でした。

おまけ

完成したコードの33行目を以下のように変更すると、同じ行でグラフが伸びたり縮んだりします

    printf("%-20s\r", '*' x $star);

Perl入学式 2019 in東京 第1回 お疲れ様でした

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

毎期、新しい挑戦をしているPerl入学式ですが、今期はカリキュラムを改版して臨んだ第1回となります。

スライドはMarkdown形式で公開しています。

www.perl-entrance.org

また、復習問題も追加しました。
あわせて復習問題の解答例も作成しております。

第1回でやった内容のみで解ける問題となっています。時間のある時や大連休の時にぜひ挑戦してみてください。

欲望ドリブン!

プログラムに限らないのですが、何かの習熟・上達には「欲望」が欠かせません。

「仕事を早く終わらせたい」「仕事を楽にしたい」「もっと稼げる仕事に就きたい」「xxxな画像を集めたい」

なんでも良いです。欲望が大事です。

そして、Perl入学式沖縄のアナグラくんのスライドが超いいので置いておきます。
みんな見て!

Hello World ~我々は如何にしてプログラミングを学習して いくべきか、その議論で見えた一筋の道標とは~ - Speaker Deck

speakerdeck.com

第1回 落ち穂拾い

昨年秋に開校した際の補足記事です。参考にご覧ください。

sironekotoro.hateblo.jp

また、テキストエディタAtom, VSCodeを使ってPerlを快適に書くための設定を書いてみました。こちらもどうぞ。

ピザ会のお題

Perl入学式 in東京 では講義終了後、希望者で集まりピザ会と称した懇親会を行っています。
(ジュース・ピザ代は実費)
昨年からそこで余興として xtetsuji さんが課題を出し、そこにチャレンジャーが回答して公開する、というようなことをやってます。

今回の出題は、「元号 -> 西暦コンバータ」でした。

Perl入学式 第1回 ピザ会 課題 2019/04/13 · GitHub

この問題を解くためには第1回で学んだ

に加えて「正規表現」が必要です。
講義中に「正規表現ができると何が嬉しいんですか?」って聞かれたやつですね。
正規表現を使うと、「文字の並びのパターンから文字列の操作ができる」というのが嬉しいところです。

以下は平成のみのコンバータですが、ここでは文字の並びのパターンを

「先頭が文字2文字で、そのあとに数字が続き、"年"で終わる文字列」

として正規表現を使っています。解説を入れたらかえって分かりにくくなるという悲しみがあります・・・

平成○○年 と入力すると西暦にして返してくれる君 — Bitbucket

本来のxtetsujiさんの問題に沿った内容で回答したのが以下です。せっかくなのでおまけも実装してみました。

  • 全角数字を入れてもok
  • 昭和をS, 平成をH, とする短縮記法でもok

和暦を引数に与えると西暦年を応えてくれる — Bitbucket

(2019年04月20日 追記) 元号から西暦を求めるクイズも作ってみました。明日に控えDBスペシャリスト試験の直前勉強があまりにも捗らなくて現実逃避した結果です

https://bitbucket.org/snippets/sironekotoro/ge5aAA

Perl入学式 in東京 2019 第2回

5月中旬以降での開催を予定しています。

待ちきれないという方、過去の講義スライドも公開しています。そちらで予習をお願いします。

2018年度の講義資料

また、復習や疑問点がある方はぜひ、Perl入学式のSlackに参加してそこで問い合わせてください。
問い合わせの際に、問題のあるコードをブログやgist(GitHub), Snippet(Bitbucket)などに貼り付けてもらえると助かります。

このブログでも、Perl入学式に関するエントリを載せていますので、参考にどうぞ。

sironekotoro.hateblo.jp

それでは、第2回でもお待ちしています!