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
- MSYSのopensslのインストールはできたが、
cpanm Net::SSLeay
は失敗
pacman -S perl-Net-SSLeay
cpanm IO::Socket::SSL
- 大文字小文字を厳密に見てくるので注意
- こっちはなんか時間切れとか出てくる。きー!
pacman -S perl-IO-Socket-SSL
- インストールできた
- うごいた!
こんな感じでした。ここまでが長い。でも、動いてしまえば「許そう、全てを・・・」って気分になりませんか。うちはなります。
HTTPクライアント編
HTTPクライアントというのは、Chromeとか、Firefoxとかそういったブラウザです。HTTPサーバーにアクセスし、情報を得るものです。
プログラムからもHTTPサーバにアクセスして情報を得ることができます。
以下はPerlでのHTTPクライアントの紹介です。
HTML::Tiny
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
ここからは 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
上記 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
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
うちはこのツールをPerl CPANモジュールガイド という本で知って、ほぼこれをメインに使っています。要素の指定にXPathとCSSセレクタを利用することができます。
この本には他にも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
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
スクレイピングツールのくくりに入れましたが、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
一部の人には刺さるかもしれません。私も刺さった人です。
今まで紹介したものは、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を利用します。
地域は東京地方、今日(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アプリから地域を選べるようにすれば・・・となります。
そんな、Webアプリからの入出力を学べる Perl入学式 2019 第5回 in東京 お待ちしております!
perl-entrance-tokyo.connpass.com
ojo
Perl入学式 in沖縄の id:AnaTofuZ さんから教えてもらったのがこのツール。
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++
うちはこんな感じで解いてみました。平均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;
今回のピザ会は盛況だった
他にも
- ライフゲーム
- Imger
- 並列処理
とネタが多くて、一通り書いてからブログ公開だとしばらくかかりそうだなーってことで、先にかいた双六から公開しまする。
Perl入学式 2019 in東京 第4回 お疲れ様でした
受講された方、サポーターの方、大変湿度の高い中、お疲れ様でした。
講師をやったジャージの人です。
Perl入学式第4回です。カリキュラム改定に伴い、Perl初学者には難しい(しかし避けては通れない)「リファレンス」で1回丸々使う回となりました。
講義に利用したスライドはMarkdown形式で公開しています。復習に使ってください。
また、講義中に数回言及しましたが、練習問題も骨のあるものになっています。ぜひチャレンジしてみてください!
問題の意味がわからない、とか、このような解答例はどうだろう?という方はSlackのPerl入学式チャンネル(招待フォーム)やtwitterでハッシュタグ #Perl入学式 をつけて聞いてみてください。
応答速度、監視頻度などの面からSlackの方をお勧めします。
リファレンス!
過去のエントリで大体語り尽くしてる感があるので、そちらで・・・
講義の最後にもオススメしましたが、Perlのリファレンスに関しては、完全な自信をもって深沢千尋さんの「すぐわかる オブジェクト指向 Perl」をお勧めします。
ピザ会
今日はネタが多くてちょっと後日にまとめて書く予定です。
いつもの id:xtetsuji さん出題、サイコロゲームの試行回数
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にインストールする方が難しい・・・ただし、一度動いてしまえば、これらの記事が大いに参考になります。
特にお世話になっている記事を紹介します。
そして、貴重な活字での情報です。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形式で公開しています。復習に使ってください。
問題の意味がわからない、とか、このような解答例はどうだろう?という方はSlackのPerl入学式チャンネル(招待フォーム)やtwitterでハッシュタグ #Perl入学式 をつけて聞いてみてください。
応答速度、監視頻度などの面からSlackの方をお勧めします。
講義中のネタについて
Perlcon 2019
東欧にあるラトビアの首都・リガでPerlCon2019が開催されます。
Perl入学式の初代校長のpapixさん、Perl入学式 沖縄のAnaTofuZさんが登壇します。すごい!
講義中に使ったスライド
自作のスライドです。
特に、最後のページにある Everything はWindowsマシン使うなら必須のソフトだと思っています。CUIはまだ抵抗がある、という方にも優しいGUIのソフトです。ぜひ使ってみてください。
お仕事での正規表現
最近の仕事で使った正規表現はこんな感じです。
特定の語を含む文書を検索し、その文書のタイトルやtimestampなどのメタ情報を収集する
# 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形式で公開しています。復習に使ってください。
また、復習問題を用意しています。
第1回と第2回でやった内容のみで解ける問題となっています。ぜひ挑戦してみてください。
復習問題の解答例もありますので、参考にしてください。
問題の意味がわからない、とか、このような解答例はどうだろう?という方はSlackのPerl入学式チャンネル(招待フォーム)やtwitterでハッシュタグ #Perl入学式 をつけて聞いてみてください。
応答速度、監視頻度などの面からSlackの方をお勧めします。
ピザ会のお題
Perl入学式 in東京 では講義終了後、希望者で集まりピザ会と称した懇親会を行っています。
(ジュース・ピザ代は実費)
昨年からそこで余興として xtetsuji さんが課題を出し、そこにチャレンジャーが回答して公開する、というようなことをやってます。
xtetsujiさんからのお題ですが、今回は「正弦波を描く」でした。
三角関数、昨年冬あたりに高校数学の復習でやっていたので
- 正弦波: sin(サイン)を使う
- 最大は 1
- 最少は -1
というところまでは知っていたのですが、それをコードに起こすロジックがわからない・・・(5分くらいは考えた)
しかし、それでも何とか、それっぽいのを・・・と頑張った結果がこれです!それっぽい何かが出力されます。
xtetsujiさん曰く「似ている、近似・・・というよりも禁じ手ですね」
こんなの世に出していいんだろうか。ってまぁ、こういうのこそ晒していきましょう。晒しただけ強くなれます(強さとは
コードにコメントも書きましたが、Perl入学式の第1回, 第2回の内容で書いています。
リベンジマッチ正弦波を描く・実況中継
そもそも、昨年冬に俺は三角関数の復習をしたんじゃなかったのか?という悔しさがあります。
俺がやったのは問題の解き方だけなのか?という反省もあります。
ということでリベンジマッチです。
まず、Perlのsin関数とは、というところを調べます。
sin EXPR
sin(ラジアンで示した) EXPR の正弦を返します。 EXPR が省略されたときには、$_ の正弦を返します。
ふむふむ。ラジアンが何かはともかく、数学用語だから数字入れてみればいいのかな?
print sin(1) . "\n" # 0.841470984807897
やはり数字が返ってきた。でも、ラジアンが何者かを理解しないとsin関数はうまく使えないみたい。ではラジアンとは?
(ここで「ラジアン 中学生」「ラジアン 高校」「ラジアン わからない」「ラジアンとは」などでググる)
ほーほー、三角関数を求める時の角度ではなく、円の半径と弧の長さから導き出されるものなのね。しかも、角度とラジアンの変換公式もある。
となると、こういう戦略はどうだろう
- $kakudo って変数を用意する。初期値は0
- whileループを用意・・・いや、無限ループが怖いからfor文にしておこう。最初は20回くらい処理を繰り返してみる
- ループ処理の中では、$kakudoを +30 していく
- $kakudoは360を超えても気にしない。2週目に入ったという感じで。スノーボードの720ターンみたいな(2回転するターン)
my $kakudo = 0; for ( 1 .. 20 ) { print $kakudo . "\n"; $kakudo = $kakudo + 30; } # 出力 # 0 # 30 # 60 # 90 # 120 # ... # 570
よさそう。では、この角度をラジアンにするロジックを考える・・・その前に、Perlにそういう関数がないかな?って調べてみる。
やっぱりあった。しかもタイトルが既にうちの求めてるものそのまま。おなじみ、木本先生のサイト。安心して参考にさせてもらう。
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 さんの模範解答を参考に。
そして完成したコードがこちら。
ということで、うちの回答リベンジ編でした。
おまけ
完成したコードの33行目を以下のように変更すると、同じ行でグラフが伸びたり縮んだりします
printf("%-20s\r", '*' x $star);
Perl入学式 2019 in東京 第1回 お疲れ様でした
受講された方、サポーターの方、お疲れ様でした。
講師をやったジャージの人です。
毎期、新しい挑戦をしているPerl入学式ですが、今期はカリキュラムを改版して臨んだ第1回となります。
スライドはMarkdown形式で公開しています。
また、復習問題も追加しました。
あわせて復習問題の解答例も作成しております。
第1回でやった内容のみで解ける問題となっています。時間のある時や大連休の時にぜひ挑戦してみてください。
欲望ドリブン!
プログラムに限らないのですが、何かの習熟・上達には「欲望」が欠かせません。
「仕事を早く終わらせたい」「仕事を楽にしたい」「もっと稼げる仕事に就きたい」「xxxな画像を集めたい」
なんでも良いです。欲望が大事です。
そして、Perl入学式沖縄のアナグラくんのスライドが超いいので置いておきます。
みんな見て!
Hello World ~我々は如何にしてプログラミングを学習して いくべきか、その議論で見えた一筋の道標とは~ - Speaker Deck
第1回 落ち穂拾い
昨年秋に開校した際の補足記事です。参考にご覧ください。
また、テキストエディタ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月中旬以降での開催を予定しています。
待ちきれないという方、過去の講義スライドも公開しています。そちらで予習をお願いします。
また、復習や疑問点がある方はぜひ、Perl入学式のSlackに参加してそこで問い合わせてください。
問い合わせの際に、問題のあるコードをブログやgist(GitHub), Snippet(Bitbucket)などに貼り付けてもらえると助かります。
このブログでも、Perl入学式に関するエントリを載せていますので、参考にどうぞ。
それでは、第2回でもお待ちしています!