Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
1577 lines (1160 sloc) 50.1 KB

Perl入学式 第2回

構文基礎編


諸注意

  • 会場について

    • 飲食・喫煙・トイレetc
  • 写真撮影について

    • 写真撮影NGな方は、お手数ですが申し出てください。

    • 写真はPerl普及団体の JPA ( Japan Perl Association )への活動報告に利用します。


講師紹介

  • 講師・サポーター紹介

皆さんで自己紹介

  • せっかく今日集まったので、テーブルで自己紹介をしましょう。

  • 話題は自由ですが、以下がオススメです。

    • 名前(ハンドルネーム)

    • なぜPerlを勉強してみようと思ったか

    • なぜPerl入学式に参加してみようと思ったか


今日の流れ

  • 前回の復習
  • 条件分岐( if文)
  • 比較演算子( <, >, ==, !=, eq, ne, gt, lt )
  • 論理演算子( &&, || )
  • 配列
  • 配列の操作( join, split, push, pop, shift, unshift, reverse, sort )

前回の復習


ターミナルからディレクトリ操作

$ cd                    # ホームディレクトリに移動する
$ pwd                   # 現在のパスを表示する
$ ls -l                 # ファイル・フォルダの一覧を表示する
$ mkdir perl-entrance   # perl-entrance というフォルダを作成する
$ cd perl-entrance      # 作成した perl-entrance フォルダに移動する
$ mkdir 2nd             # 2nd というフォルダを作成する
$ cd 2nd                # 作成した 2nd というフォルダに移動する

「ホームディレクトリ」とは、ユーザごとに用意された作業場所のようなものです (第1回 第2部)

perl-entrance ディレクトリがすでにある方は、4行目の mkdir perl-entrance は省略してください。

今日作成するスクリプトは、この 2nd ディレクトリの中に入れていくと良いでしょう。


おまじない

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

    • Perlの実行プログラムの場所を指定する
  • use strict

    • 厳密な書式を定めたり、未定義の変数を警告する
  • use warnings

    • 望ましくない記述を警告する
  • 以下、この資料のサンプルコードでは紙面の都合上「おまじない」を省略しますが、皆さんは必ず記述してください


Hello, World!(hello.pl)

print "Hello, World!\n";
  • このように書いたものを、hello.pl として保存します。

  • print は、ターミナルに文字を出力します。

  • Hello, World! はダブルクォーテーション " " で囲みます。

  • \n は改行を表します。

  • 行の最後に ; を忘れずに!

  • そして「おまじない」の3行も忘れないこと!


Hello, World! の実行

$ perl hello.pl

Hello, World!
  1. ターミナルを開きます。

  2. perl から1つスペースを空けて hello.pl と入力してenterキーを押します。

    • プログラムに渡される情報、値などを 引数ひきすう といいます。

    • この例では hello.pl が引数になります。

  3. プログラムの中に書いた Hello, World が表示されます。


スカラー変数


スカラー変数

変数とは?

  • 変数へんすう」は、いわば「データを格納できる箱」です。

  • Perlの変数には、「スカラ変数」、「配列はいれつ」、「連想配列れんそうはいれつ(ハッシュ)」という3つの種類があります。

  • 3種類の変数は、それぞれの変数の型を示す シジル(sigil) という記号で区別されます。

    1. スカラ変数($):1つの要素を格納する。

    2. 配列(@):複数の要素を「順番に」格納する。

    3. ハッシュ(%):複数の要素を「名前 => 値」の組み合わせで格納する。

  • 配列は第2回、ハッシュ(連想配列)は第3回で解説します。


スカラー変数

スカラー変数・・・その前に「コメント」

print "Hello World!";   # -- ここがコメント部 --

# print "Hello! Perl Entrance!"; 行頭に # があるため、printは実行されない
  • 各プログラム言語にはスクリプトやプログラムをわかりやすくするために、コメントを入れる仕組みがあります。

  • Perlでは、 # より右にある文字列は全てコメントとして扱われます。

  • コメントは実行されません。

  • この資料の中でも、 コメントを使ってコードに補足を行います。


スカラー変数

スカラー変数を利用する

my $foo;    # 変数を宣言する
$foo = 1;   # 右辺の 1 を左辺の $foo に入れる
print "$foo\n";
  • 変数を初めて使うときは、先頭に my をつけます。変数が利用できるようにすることを「変数を宣言する」といいます。

  • スカラ変数の先頭には、$foo のように $ をつけます。 $スカラ(scalar)S 、と覚えるとよいでしょう。

  • = という記号(代入演算子)を使うことで、「右辺の値」を「左辺のスカラー変数 $foo 」に入れる(代入する)ことができます。


スカラー変数

スカラー変数を利用する

my $foo;    # 変数を宣言する
$foo = 1;          # 右辺の 1 を左辺の $foo に入れる
print "$foo\n";    # 1

$foo = "Hello World";   # 宣言済みなので my は不要
                        # 右辺の "Hello World" が 左辺の $foo に入る
print "$foo\n";         # Hello World
  • 宣言した変数を2回目以降に使うときは、 my は不要です。my は変数を宣言する最初のみ必要です。

  • 値が入っているスカラー変数に別の値を代入すると、中身が上書きされます


スカラー変数

仮の変数名(メタ構文変数)

my $foo;
my $bar = 1;
$bar = "Hello World";
  • プログラム言語の参考書には以下の単語が変数名として利用されることがあります。

    • foo, bar, baz

    • hoge, piyo, fuga

  • これら「意味のない名前」をつけることで、「サンプル用」「例示用」の変数ということを明示するためです。

  • このような単語を使った変数を「メタ構文変数」と呼称します。

  • これらの変数名は現場のコードでは利用しないようにしましょう。


スカラー変数

クォーテーションによる表示の違い

my $foo = 1;

print "$foo\n"; # ダブルクォーテーションで囲うと「1」が表示される

print '$foo\n'; # シングルクォーテーションで囲うと「$foo\n」が表示される
  • print を使うことで変数を表示することができます。

  • 変数を表示する場合は、 " "(ダブルクォーテーション)の中に変数を書きましょう。

    • " " の中に変数を書くと、その変数の中身が表示されます。

    • ' ' の中では、変数の中身ではなく、書いてあるままに表示されます。


スカラー変数

エラーチェック

変数を使い始めると、プログラムの行数が増え、エラーが入り込むことが多くなってきます。

perlでは perl -wc hoge.plwcオプションを付けることで、スクリプトを実行する前に、Perlの文法に合っているかどうかのチェックが可能です。

$perl -wc foo.pl

foo.pl syntax OK    # この表示が出れば、文法上の問題はない

スカラー変数

エラーメッセージ

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

my $hoge = "Hello"  # "hello"の後にセミコロンがない
print "$hoge\n";
  • よくありがちなエラーとして、行末の「セミコロン ; を忘れる」というものがあります。
  • 先程のシンタックスチェックや、お約束の3行を書いた場合は次のようなエラーがでます。
syntax error at foo.pl line 7, near "print"
Execution of foo.pl aborted due to compilation errors.
  • この場合、7行目のprintの周辺でシンタックスエラーが発生しているという意味になります

スカラー変数

エラー文は怖くない

Perlに限らず、プログラムのエラーメッセージのほとんどは英語で表示されます。

最初は戸惑うかもしれませんが、エラーメッセージ中にある行番号を手がかりに直前・直後の行でおかしいところを探してみましょう。

また、エラーメッセージそのものをGoogleなどで検索したり、翻訳することでエラー解決への手がかりを掴むこともできます。

エラーが出るのは悪いことではありません。直すべき場所を明示してくれるありがたい指針です。

エラーを出さずにプログラムを習得することはできません。

落ち着いて対処していきましょう。


スカラー変数

練習問題 (scalar_practice.pl)

  • Perl入学式とYAPCの情報をスカラ変数に格納し、それらの値を表示するスクリプトscalar_practice.plを作成しよう!

    • seminar_name この勉強会の名前
    • date 今日の日付
    • venue_name 会場名
    • pre_yapc_pref 前回のYAPCの場所
  • 「お約束」の3行を忘れずに!


四則演算と文字列連結


四則演算と文字列連結

基本的な演算子(足し算・引き算)

my $foo = 4;
my $bar = 2;
my $result;

# 足し算 +
$result = $foo + $bar;    # 足し算 して $result に代入する
print "$foo + $bar = ";   # 式の表示だけ。改行はしない。
print "$result\n";        # 上の式に続けて $result を表示する

# 引き算 -
$result = $foo - $bar;    # 足し算 して $result に代入する
print "$foo - $bar = ";   # 表示だけ。改行はしない。
print "$result\n";        # 上の式に続けて $result を表示する
  • 算数と同じ + と - が利用できます。

四則演算と文字列連結

基本的な演算子(掛け算・割り算)

my $foo = 4;
my $bar = 2;
my $result;

# 掛け算 * (アスタリスク)
$result = $foo * $bar;    # 掛け算 して $result に代入する
print "$foo * $bar = ";   # 表示だけ。改行はしない。
print "$result\n";        # 上の式に続けて $result を表示する

# 割り算 / (スラッシュ)
$result = $foo / $bar;    # 足し算 して $result に代入する
print "$foo / $bar = ";   # 表示だけ。改行はしない。
print "$result\n";        # 上の式に続けて $result を表示する

四則演算と文字列連結

基本的な演算子(べき乗・剰余)

my $foo = 4;
my $bar = 2;
my $result;

# べき乗 **
$result = $foo**$bar;       # 4 の 2乗 を $result に代入する
print "$foo ** $bar = ";    # 式の表示だけ。改行はしない。
print "$result\n";          # 上の式に続けて $result を表示する

# 剰余 %
$result = $foo % $bar;     # 4 を 2 で割った余りを $result に代入する
print "$foo % $bar = ";    # 表示だけ。改行はしない。
print "$result\n";         # 上の式に続けて $result を表示する

四則演算と文字列連結

剰余の使い所

剰余じょうよは、初めて聞く言葉かもしれません。利用例を解説します。

print 100 % 7;    # 100 / 7 の余りである 2

例えば「今日の100日後の曜日」を求めるとき、100日分カレンダーを辿るのは大変です。

1週間は7日なので、7日後は同じ曜日となります。14日後も同じ曜日となります。21日、28日、35日... 7の倍数日後は同じ曜日になります。

これを利用し、100日 を 7日 で割ってみます。剰余は 2 となります。

明日の曜日と同じであれば剰余は 1、 明後日と同じ曜日であれば 2、 と簡単に計算が可能です。


四則演算と文字列連結

代入演算子の性質

my $foo = 4;

$foo = $foo * 3;  # 右辺の計算結果( 4 * 3 )が、左辺の $foo に代入される

print "$foo\n";   # 12
  • 代入演算子 = は、算数のイコールとは性質が異なります。

    • 算数   :左辺と右辺が等しいことを示す

    • プログラム:右辺の計算結果を左辺に代入する

  • 上記の例では右辺の「 $foo * 3 」つまり 12 が = の左辺 $foo に代入されます。


四則演算と文字列連結

省略記法

代入演算子( = )を利用して、変数の中身を書き換える処理は多いため、専用の記法が用意されています。

my $fuga = 100;
my $hoge = 50;

$fuga += 1;         # $fuga = $fuga + 1;と同じ
print "$fuga\n";    # 101
$fuga -= 1;         # $fuga = $fuga - 1;と同じ
print "$fuga\n";    # 100

$fuga *= 2;         # $fuga = $fuga * 2;と同じ
print "$fuga\n";    # 200
$fuga /= $hoge;     # $fuga = $fuga / $hoge と同じ
print "$fuga\n";    # 4

四則演算と文字列連結

インクリメント

1 ずつ加算することを「インクリメント」といいます。この処理も多用するので、専用の記法が用意されています。

my $foo = 4;

$foo = $foo + 1;# 代入演算子を利用
print "$foo\n"; # 5

$foo += 1;      # 省略記法
print "$foo\n"; # 6

++$foo;         # 前置インクリメント
print "$foo\n"; # 7

$foo++;         # 後置インクリメント
print "$foo\n"; # 8

前置と後置の違いはこちらを参照


四則演算と文字列連結

演算の順番

my $foo = 2 + 4 * 3;
print "$foo\n";    # 14

my $bar = ( 2 + 4 ) * 3;
print "$bar\n";    # 18
  • 演算の順序は、 ( ) を付けることで変更することができます。

  • 通常の数学と同じく、Perlも「足し算・引き算」より「掛け算・割り算」の優先順位が高いです。

  • 2行目では ( ) を付けることで優先順位を変え、「足し算」を先に計算させています。


四則演算と文字列連結

文字列連結

my $foo = 'hoge';
my $bar = 123;

print "$foo$bar\n";         # hoge123

print $foo . $bar . "\n";   # hoge123

my $buz = $foo . $bar;
print $buz . "\n";          # hoge123
  • . (ドット)で文字列や変数を連結することができます。

  • 連結した文字列や変数を、別の変数に代入することも可能です。


四則演算と文字列連結

数値演算と文字列連結は分ける

演算の優先順位と結合方向(演算の向き)はperldocのperlopの項に詳細が記載されています。

ただし、これを覚えるのも、他の人に読んでもらうのも大変な労力です。

( )で演算の順番を明確に示す、数値演算と文字列の連結は分けて書くなど、わかりやすい記述を心がけましょう。


四則演算と文字列連結

数値演算と文字列連結の混合

数値演算と文字列連結を混ぜた場合の「アンチパターン(良くない例)」を示します。

my $now                 = 2019;
my $start_perl_entrance = 2012;
print "Perl入学式は開講" . $now - $start_perl_entrance . "年です\n"; # エラーメッセージの後に -2012年です

「-2012年です」と、おかしな結果になります。これは演算子の優先順位が .- で同じであり, 左結合のため, 左から演算されるためです.

  1. まず 「Perl入学式は開講」(文字) と 「2019」(数字) が文字連結されます。(結果:「今年は開講2019」)

  2. 次に、「今年は開講2019」(文字) から 「2012」(数字)を引き算しようとし、おかしな結果になります。


四則演算と文字列連結

数値演算と文字列連結は分ける

文字と数値計算を明確に分けてから、 計算してから連結するのが確実です.

my $now                 = 2019;
my $start_perl_entrance = 2012;
print "Perl入学式は開講" . ($now - $start_perl_entrance ). "年です\n"; # Perl入学式は開講7年です

my $period = $now - $start_perl_entrance;
print "Perl入学式は開講" . $period . "年です\n"; # Perl入学式は開講7年です
  • 1番目のprint文のように、( )で演算の順序を変更することで、正しい結果になります。

  • 2番目のように、計算結果を別の変数に入れてから連結するのも良いでしょう。


四則演算と文字列連結

練習問題(sansuu1.pl)

以下の問題をPerlの四則演算を使って、変数を使わずに解いて表示してください。

  • 1+2+3+4+5+6+7+8+9+10

  • 100 ー 200

  • 1000円の品物を消費税8%で買った時の税込価格

  • 半径5mの円の面積(円周率は3.14とする)


練習問題(sansuu2.pl)

以下の問題をPerlの四則演算を使って解いてください。

my $pizza = 10 を使って解いて表示してください

  • 10個のピザを5人で分けた時の、一人当たりの個数

  • 10個のピザを3人で分けた時の、余りの数


標準入力


標準入力

入力とは?

  • これまでは、あらかじめスクリプトに書かれた文字や数字を表示してきました。

  • スクリプトの実行の都度、中身を変える方法として「入力」を用いる方法があります。

  • プログラムやコンピュータに様々な数値や文字列を与えることを「入力」と言います。

    • キーボードからの入力、ファイルからの入力、音声入力、カメラ、画面へのタッチ・・・
  • 様々な入力のうち、キーボードから入力されるテキストデータのことを標準入力ひょうじゅんにゅうりょく(Standard Input)といいます。

  • 入力の終わりはEnterキー 又は returnキー となります。


標準入力

Perlで標準入力を受け取る

my $str = <STDIN>;  # 標準入力から文字列を受け取り、変数 $str に代入する
chomp $str;         # $strの中の末尾の改行を削除する
print $str;         # $strを表示する
  • Perl では、この「標準入力」をスクリプトの中で <STDIN> と表現します。

  • 1行目では、 <STDIN> で端末から入力された文字列を受け取り、受け取った文字列を変数 $str に代入しています。

    • 文字列(Strings)を表す変数として $str がよく利用されます。
  • 2行目では chomp で行末の改行を削除しています。

    • <STDIN> は改行が含まれているため、必須の処理です。

標準入力

Perlで標準入力を受け取る

print 'input Season > ';    # 入力を促すメッセージ

my $season = <STDIN>;   # 標準入力 <STDIN> で入力されたものを $season に入れる

chomp $season;   # $season に格納された入力から、末尾の改行を削除する

print "Alice loves $season\n";    # 変数を表示して改行する
  • このコードを実行して、 <STDIN> がどのような働きをするか見てみましょう。

  • このように、標準入力によってスクリプトの実行結果を変えることが可能です。


標準入力

練習問題 (hello_stdin.pl)

Your Name?> Taro
Hello Taro!
Your OS?> macOS
macOS is useful OS!
  • "Your Name?> " と画面に表示して名前の入力を求め、入力された名前の左側に Hello、右側に "!" を付けて表示するスクリプト hello_stdin.pl を作成しよう。

  • さらに続けて "Your OS?> " と画面に表示して使用中の OS の入力を求め、入力されたOS名の右側に "is useful OS!" を付けて表示させるようにもしてみましょう。


標準入力

練習問題 (calc.pl)

標準入力<STDIN>から0以外の整数を2つ読み込み、 それらを四則演算(+, -, *, /)した結果を上の例のように表示するスクリプト calc.plを作成しよう。

最初に1、次に2を入れた場合、以下のように表示されるスクリプトです。

1 + 2 = 3
1 - 2 = -1
1 * 2 = 2
1 / 2 = 0.5

if文


if文

条件分岐

スクリプトは上から下へ順番に処理されていきます。

しかし、変数の状態に応じてスクリプトの動きを分岐させることができます。これを 条件分岐 といいます。

条件分岐には様々なパターンがありますが、それらを の2つの組み合わせで表現します。

真 または 偽 の判断に用いる式を 条件式 といいます。


if文

条件式

条件式、と言っても難しいことではありません。

  • 信号が青なら、横断歩道を渡っても良い
  • 信号が青でないなら、横断歩道を渡ることはできない

これも立派な条件分岐です。

この場合の条件式は「信号が青であるか、青でないかを判断する」ものです。


if文

Perlにおける偽値

条件式の結果は 真 または 偽 となります。Perlでは、次の5つの値が「偽」の値として扱われます。

  1. 数値「0
  2. 文字列「'0'
  3. 文字列「''」(シングルクォーテーションの連続、間に何もない。空文字ともいう。)
  4. 空のリスト「()」 Perl入学式では扱わない
  5. undef(まだ値を設定していないスカラー変数)Perl入学式では扱わない

これ以外の値は、Perlでは全て「真」として扱われます。例示などで真の値が必要なときは数字の 1 が利用されます。


if文

Perlにはtrue/falseがない

他のプログラミング言語では、truefalse という単語に 真 または 偽 の意味を持たせたキーワードとして扱われることがあります。

しかし、Perlは truefalse などで真偽値を表すことはできません。

また特別な意味もありません。

あくまで、Perlでは先に述べた5つの偽値のみが偽、それ以外はすべて真となります。


if文

if文の構文(擬似コード)

Perlのif文はこのような構造です。

if ( 条件式 ) {
    条件式の結果が 真 の場合に実行されるコード;
}
else {
    条件式の結果が 偽 の場合に実行されるコード;
}

先の青信号の例で例えてみます。

if ( 信号が青だ ) {
    信号を渡る
}
else {
    信号を渡らない
}

if文

if文の構文

Perlのコードです。

if ( 1 ) {           # 条件式の結果が明示的な「真」の値である 1
    print "OK\n";    # 条件が「真」なので出力される
}
else{
    print "NG\n";    # 実行されない
}
if ( 0 ) {           # 条件式の結果が「偽」の値である 0
    print "OK\n";    # 実行されない
}
else{
    print "NG\n";    # 条件が「偽」なので出力される
}

if文

数値の比較演算子

if文は条件式によって実行結果が変わります。この条件式の中で利用するのが 比較演算子 です。まずは数値の比較演算子から紹介します。

== # 右辺と左辺が等しいならば「真」
!= # 右辺と左辺が等しくないならば「真」

<  # 右辺より左辺が小さいならば「真」
>  # 右辺より左辺が大きいならば「真」

<= # 右辺が左辺以上ならば「真」
>= # 右辺が左辺以下ならば「真」

数値同士を比較するときは、このような記号を用いた比較演算子を使います。


if文

数値を使った条件分岐

my $foo = 1;
if ( $foo == 1 ) {
    print "OK\n"; # こちらが表示される
}
else {
    print "NG\n";
}

条件式は ( ) の中の $foo == 1 です。

==右辺と左辺が数値として等しければ真 であることを意味します。

条件式内で $foo1 と等しい場合に「真」となり、OK という文字が表示されます。 $foo1 と等しくない場合は、「偽」となり else に処理が移って NG という文字が表示されます。


if文

よくあるまちがい (1)

my $foo == 1; # NG

my $bar =  1; # OK

数値の比較に用いる == と、代入の = を間違えています。

この場合、実行すると以下のエラーが出ます。

Useless use of numeric eq (==) in void context

「おまじない」の3行を忘れた場合はエラーが出ずに危険です。


if文

よくあるまちがい (2)

my $foo = 1;

if ($foo =  1) { print $foo } # NG

if ($foo == 1) { print $foo } # OK

条件式に注目してください。

代入の = と、数値の比較に用いる == を間違えています。

この場合、実行すると以下のエラーが出ます。

Found = in conditional, should be ==

「おまじない」の3行を忘れた場合はエラーが出ずに危険です。


if文

文字列の比較演算子

文字列を比較する場合は eqne といった単語を用いた比較演算子を使います。

eq  # equal
ne  # not equal

gt  # greater than
ge  # greater equal

lt  # less than
le  # less equal

「文字列の大小」とは、文字列の長さではありません。

辞書順に並べた時に前にくるものが小さく、後ろにくるものが大きいという意味です。


if文

文字列を使った条件分岐

条件式に注目してください。

my $foo = 'hello';

if ( $foo eq 'hello' ) {
    print "OK\n";   # こちらが表示される
}
else {
    print "NG\n";
}

文字列を比較しているので、 == ではなく eqを用いています


練習問題(question_word.pl)

#!/usr/bin/env perl
use strict;
use warnings;
my $answer = 'perl';    # 好きな文字を入力しておく

簡単な文字当てゲーム question_word.plを作成しよう。

  1. ターミナルから標準入力で文字列を一つ入力します。

  2. その文字列が $answer と一致したらOKと表示します。

  3. 外れたら NG と表示します。


if文

数値 と 文字列 の比較を混合させる
アンチパターン

print "eq で評価すると、";
if ( '51.0' eq 51 ) {
    print "文字列'51.0' と 数値 51 は等しい\n";
}
else {
    print "文字列'51.0' と 数値 51 は等しくない\n"; # こちらが表示される
}

eq は文字列を比較するときに使う比較演算子です。

このため、条件式の左辺・右辺は文字列として 異なる と判断されます。


if文

数値 と 文字列 の比較を混合させる
アンチパターン

print "== で評価すると、";
if ( '51.0' == 51 ) {
    print "文字列'51.0' と 数値 51 は等しい\n"; # こちらが表示される
}
else {
    print "文字列'51.0' と 数値 51 は等しくない\n";
}

条件式の比較演算子を eq から == に変更すると、先ほどとは逆の結果となります。

== は数値を比較するときに使う比較演算子です。

このため、条件式の左辺・右辺は数値として 等しい と判断されます。


if文

複数の条件分岐

elsifを使うことで、分岐条件を増やすことができます。

my $foo = 1;

if ( $foo == 1 ) {
    print '$foo is One' . "\n"; # これが表示される
}
elsif ( $foo == 2 ) {
    print '$foo is Two' . "\n";
}
elsif ( $foo == 3 ) {
    print '$foo is Three' . "\n";
}
else {
    print '$foo is not even One, even Two, even Three' . "\n";
}

else if ではなく、 elsif です。


論理演算子


##論理演算子

論理演算子 AND, OR

if文 の条件式で複雑な条件を扱いたいときは 論理演算子 を用いて複数の条件を連結します。

&& # かつ

|| # または
  • && 演算子は「かつ・AND」の意味を持つ。両辺の条件が 真 となるときのみ 真 となる。

  • || 演算子は「または・OR」の意味を持つ。両辺のうち、片方が 真 ならば 真 となる。


論理演算子

AND

条件式に注目してください。

my $foo = 10;

if ( $foo > 0 && $foo % 2 == 0 ) {  # $fooが 0 より大きい かつ 2 で割り切れる
    print "&& : OK\n";    # こちらが表示される
}
else{
    print "&& : NG\n"
}
  1. && の左辺にある$foo が 0 より大きいので、$foo > 0 を満たす。
  2. && の右辺にある$foo を 2 で割った剰余は 0 なので、$foo % 2 == 0 も満たす。
  3. 「1. かつ 2. が 真」の条件を満たすので、条件式は 真 となり、&&: OK と表示される。

論理演算子

OR

条件式に注目してください。

my $foo = 10;

if ( $foo > 0 || $foo % 2 == 1 ) {  # $fooが 0 より大きい または 2 で割り切れない
    print "|| : OK\n";     # こちらが表示される
}
else{
    print "&& : NG\n"
}
  1. $foo が 0 より大きいので、 $foo > 0 を満たす。
  2. 「1. または 2. が 真」の条件のうち、「1. が 真 」を満たすので、条件式は 真 となり、||: OK と表示される。

論理演算子

3つ以上の値を比較したい場合

my $small  = 10;
my $medium = 20;
my $large  = 30;

if ( $small < $medium < $large ) {    # 間違った条件式!
    print "小さい順に並んでます\n";
}

$small < $medium < $large のよう、に3つ以上の値を同時に比較することはできません。

下記のようなエラーとなります。

syntax error at sample.pl line 5, near "$medium <"

論理演算子

3つ以上の値を比較したい場合

my $small  = 10;
my $medium = 20;
my $large  = 30;

if ( $small < $medium && $medium < $large ) {
    print "小さい順に並んでます\n";
}

$small < $medium かつ $medium < $large というように、論理演算子を使って比較します。


練習問題(question_num.pl)

#!/usr/bin/env perl
use strict;
use warnings;
my $answer = 10;    # 好きな値を入力しておく

簡単な数当てゲーム question_num.plを作成しよう。

ターミナルから数字を一つ入力し、その数字とスクリプト中の $answer と比較して以下のように表示してください。

  • $answer と一致したら OK

  • $answer より大きければ too big

  • $answer より小さければ too small

  • (余裕があれば)入力した値が $answerから -5 〜 +5 の範囲内(例えば、$answerが10なら 5〜15)の場合、nearと表示する。


配列


配列

配列とは

今まで学んできた変数はスカラー変数という、1つの値のみを格納する変数でした。

例えば、小学校の4教科の平均点数を出すとき、$kokugo, $sansuu, $shakai, $science と都度スカラー変数を4つ書いて計算するのは大変です。

そこで、「複数の値」や「複数のスカラー変数」をまとめて扱うことのできる 配列、配列変数 を紹介します。


配列

配列の作り方(右辺)

まずは、右辺に注目してください。

my @array = ( 1, "foo", 3 );

( ) の中の 1, "foo", 3 それぞれを 要素 といいます。要素は , で区切られています。

右辺の ( 1, "foo", 3 ) の部分を リスト といいます。

このリストはスクリプトに書かれている通りの「要素の集合」でしかなく、スクリプトが加えたり減らしたりすることができません。

そこで、このリストを配列に代入することで、Perlが配列内の要素を操作することが可能になります。


配列

配列の作り方(左辺)

左辺に注目してください。

my @array = ( 1, "foo", 3 );

スカラー変数と同じく、初めてその配列を使うときは my で宣言します。

同様に = 演算子を使うことで、右辺のリストを左辺の配列に代入することができます。

配列は変数名の前に @ をつけます。

@アレイ(array)@rray 、と覚えるとよいでしょう。 (arrayは配列を意味する英単語です)


配列

配列の展開

my @array = ( 1, "foo", 3 );

print "@array" . "\n";    # 1 foo 3

配列はスカラー変数と同じく " " ダブルクォーテーションで囲むことで変数展開が可能です。


配列

配列の要素を利用する

配列の要素を利用する方法です。

my @array = ( 1, "foo", 3 );

print "$array[0] . "\n";    # 1

配列 @array の最初の要素は $array[0] となります。

この時、シジルが配列を示す @ からスカラー変数の $ になっていることに注目してください。

これは 取り出す要素の数が1つだけなのでスカラー変数となる ためです。

配列から複数の要素を取り出すときには配列スライスを利用します。この時のシジルは @ になります。Perl入学式では説明しません。


配列

添字を使って要素を利用

配列から要素を利用する際には、 添字そえじ を使います。

my @array = ( 1, "foo", 3 );

print "$array[0]\n";    # 1
print "$array[1]\n";    # foo
print "$array[2]\n";    # 3

配列の1つ1つの要素にアクセスする場合は $変数名[添字] を使います。

配列の添字は数字で指定します。

配列の先頭の要素の添字は 0 となり、末尾に向けて 1 ずつ増えます。


配列

配列要素の添字

配列の添字には、数値が入ったスカラー変数を置くこともできます。

my @array = ( 1, "foo", 3 );
my $i = 1;

print "$array[$i]" . "\n";    # foo

配列

配列要素への代入

配列の要素に代入することも可能です。

my @array = ( 1, "foo", 3 );
$array[0] = "bar";    # 先頭の要素に文字列 "bar" を代入する

print "@array" . "\n";       # bar foo 3

配列に新たな要素を代入する際は、取り出すときと同様に添字を使って要素を指定します。

配列に、別の配列の中身を追加することもできます。

my @array_one_two = ( 1, 2 );
my @array_numbers = ( @array_one_two, 3 );
print "@array_numbers" . "\n";    # 1 2 3

配列

配列要素の計算

配列の要素はスカラ変数と同じように扱うことが可能です。

my @array = ( 1, "foo", 3 );

my $sum = $array[0] + $array[2];     # my $sum = 1 + 3

print "$sum\n"    # 4

添字を利用して配列の要素同士で足し算をしています。


配列

配列の要素数

my @array = ( 1, "foo", 3 );

my $count_array_element = scalar @array;    # 要素の数を取得
print "$count_array_element\n";             # 3

my $last_array_index = $#array;             # 配列の末尾の要素の添字を取得
print "$last_array_index\n";                # 2
  • scalar 関数の引数に配列を与えると、配列に格納されている 要素の個数 を取得できます。

  • 配列@array に対して $#array と書くことで、配列に格納されている 末尾の要素の添字 を取得できます。


配列

範囲演算子

連続する数字を配列に格納するときは 範囲演算子 .. が便利です。

my @array1 = ( 1 .. 5 );
print "@array1" . "\n";    # 1 2 3 4 5
  • 1 .. 5 と書くことで、1 から 5 までの連続する値を配列に代入できます。

  • ただし「左側の数値より右側の数値が大きい」必要があります。

my @array2 = ( 5 .. 1 );
print "@array2\n";              # (何も表示されない)
print scalar @array2 . "\n";    # 0

配列

for ループ

配列の要素を、順番に「すべて」処理する方法として、for文 があります。

my @array = ( 1, "foo", 3 );

for my $element (@array) {
    print "$element\n";  # 1 foo 3 がそれぞれ改行されて表示される
}

この場合、変数 $element に 配列 @array の先頭から順番に要素が格納されていきます。

最初に $array[0] , $array[1], $array[2] とスカラー値が順番に代入され、処理(print)が行われます。


配列

forループと範囲演算子

for文と範囲演算子を組み合わせることで、決まった回数だけ処理を繰り返すことができます。

  • カウントアップ
for my $i ( 1 .. 50 ) {
    print "$i\n";    # 1 から 50 まで 1つずつ改行されて表示される
}
  • 単なる繰り返し
for my $i ( 1 .. 50 ) {
    print "Hello, World\n";    # Hello, World が 50回表示される
}

練習問題(for.pl)

  1. 配列 @array に好きな数値や文字列を設定し、for文で出力してください。

  2. <STDIN> を用いて配列 @array2 に好きな数値や文字列を入力し、for文で出力してください。

    • 2つの入力値を得る場合、配列の要素として <STDIN> を直接書くことはできません。

      my @array = ( <STDIN>, <STDIN> );    # NG
    • 1つずつスカラー変数で受け取り、chomp をした後で配列に入れましょう。


練習問題(even.pl)

  • for文を使って 1 から 100 までを出力してみましょう。

  • 次に 1 から 100 までのうち、偶数のみを出力するようにプログラムを変えてみましょう。

    • ヒント: Perlでは計算の剰余を求める % 演算子があります。

    • 例えば 5 % 3 は 5 を 3 で割った余りですので 2 が求まります。


練習問題(fizzbuzz.pl)

1から100までの数字について、以下のようなルールに従って表示を行う fizzbuzz.pl を作成しましょう。

  • その数字が 3 で割り切れるなら Fizz と表示する。
  • その数字が 5 で割り切れるなら Buzz と表示する。
  • その数字が 3 でも 5 でも割り切れるなら FizzBuzz と表示する。
  • その数字が 3 でも 5 でも割り切れないなら その数字 を表示する。

コンピュータサイエンス学科卒業生の過半数にはそれ(fizzbuzz)ができないのだ
どうしてプログラマに・・・プログラムが書けないのか?


配列の関数


配列の関数

join, split

関数かんすう とは、ある働きをもつ機能のことです。

いままで変数の中身を表示してきた print も関数です。

配列に関係する関数を、まずは2つ紹介します。

  • join

    • リストや配列の要素を連結してスカラー値にする
  • split

    • 文字列やスカラー変数の値を分割して、リストにする

配列の関数

join

join はリストや配列の要素を連結して、スカラー値にする関数です。

my @words = ( 'I', 'Love', 'Perl.' );    # 配列を作る
my $poem  = join '_', @words;            # 第1引数 _ , 第2引数 @words
print $poem . "\n";                      # I_Love_Perl.
  • join が受け取る第1引数 (上の例では _ アンダースコア) は、リストや配列の要素をくっつける糊のような役割を果たします。

  • 第2引数には、対象の配列やリストを渡します。


配列の関数

split

split は指定したパターンに従ってスカラー値や文字列を分割し、リストにします。

my $poem  = 'I_Love_Perl.';
my @words = split /_/, $poem;
print "@words\n";    # ('I', 'Love', 'Perl.')
  • split が受け取る第1引数 (上の例ではアンダースコア) は、文字列を分割する区切りのような役割を果たします。

  • 第2引数には、対象の文字列を渡します。

  • / / は正規表現リテラルと呼ばれるものです。この二つのスラッシュ / の間 にあるもので文字列を分割します。上記の例では、アンダースコア(下線)で文字列を区切っています。


練習問題 (1/2)

次の処理をする join.pl を作りましょう。

  1. ("0120", "123", "XXX") という配列を作ってください。

  2. 作成した配列をjoin関数を利用して - で連結して表示してください。


練習問題 (2/2)

次の処理をする split.pl を作りましょう。

  • "There's more than one way to do it." という文字列を作り、split関数で " "(半角スペース)ごとに分割して配列 @array に格納し、すべて出力してください。

配列の関数

push, pop, unshift, shift

引き続き、配列の操作を学んでいきます。

配列やリストの用途は主に2つです。

  • 名簿など、要素の 単純な集まり として表現したい場合。

  • ランキングなど、要素間の 順序 関係を表現したい場合。

これらの用途においては、要素の順番の並べ替えや、要素の追加、取り出しが必要になります。

まずは追加と取り出しを学んでいきます。


配列の関数

要素の追加と削除

  • 要素の追加

    • push
    • unshift
  • 要素の取り出し

    • pop
    • shift

配列の関数

追加と取り出しの関係

追加 取り出し
末尾 push pop
先頭 unshift shift

配列の関数

push / pop

push / pop は配列の末尾の値を操作する関数です。

my @array = ( 'Alice', 'Bob' );
push @array, 'Chris';    # 末尾に要素 Chris を追加する
print "@array" . "\n";   # Alice Bob Chris

my $element = pop @array;    # 末尾の要素 Chris を取り出す
print "@array" . "\n";       # Alice Bob
print "$element" . "\n";     # Chris
  • 配列の 末尾に要素を追加 するときには push を利用します。

  • 配列の 末尾から要素を取り出す ときには pop を利用します。


配列の関数

unshift / shift

unshift / shift は配列の先頭の値を操作する関数です。

my @array = ( 'Alice', 'Bob' );
unshift @array, 'Chris';    # 先頭に要素 Chris を追加する
print "@array" . "\n";      # Chris Alice Bob

my $element = shift @array; # 先頭の要素 Chris を取り出す
print "@array" . "\n";      # Alice Bob
print "$element" . "\n";    # Chris
  • 配列の 先頭に要素を追加 するときには unshift を利用します。

  • 配列の 末尾から要素取り出す ときには shift を利用します。


練習問題 (1/2)

次の処理をする array_pop_shift.pl を作りましょう。

  1. ('Alice', 'Bob', 'Chris') という配列 @array を作ってください。

  2. 配列 @array から 'Chris' を取り出し、出力してください。

  3. 配列 @array から 'Alice' を取り出し、出力してください。


練習問題 (2/2)

次の処理をする array_push_unshift.pl を作りましょう。

  1. ('Alice', 'Bob', 'Chris') という配列 @array を作ってください。

  2. 配列 @array の末尾に Diana を追加し、 ('Alice', 'Bob', 'Chris', 'Diana') という配列を作ってください。

  3. 配列 @array の最初に Eve を追加し、 ('Eve', 'Alice', 'Bob', 'Chris', 'Diana') という配列を作ってください。

  4. 配列をfor文で出力してみましょう。


配列の関数

reverse

reverse はリストを逆順に並べ替えて、そのリストを返す関数です。

my @lang     = ( 'perl', 'php', 'ruby', 'python', 'java', 'go');
my @reversed = reverse @lang;
print "@reversed" . "\n";    # go java python ruby php perl

配列の関数

配列の関数 reverse

連番を逆順で配列に格納したいときは、範囲演算子と組み合わせると便利です。

my @array = reverse( 1 .. 5 );

print "@array" . "\n";    # 5 4 3 2 1

配列の関数

文字列sort

sort は配列をルール順に並べ替えて、その配列を返します。

sortのみ、あるいは sort { $a cmp $b } @array と書くと、「文字列」として昇順に( a から z へ)並べ替えます。

my @lang        = ( 'perl', 'php', 'ruby', 'python', 'java', 'go' );
my @sorted_lang = sort @lang;
print "@sorted_lang" . "\n";    # go java perl php python ruby

my @num        = ( 5, 200, 40, 3, 1 );
my @sorted_num = sort @num;
print "@sorted_num" . "\n";     # 1 200 3 40 5

配列の関数

数値sort

sort { $a <=> $b } @array と書くと、「数値」として昇順(小さい順)に並べ替えます。

my @num = ( 5, 200, 40, 3, 1 );
my @sorted = sort { $a <=> $b } @num;

print "@sorted" . "\n";    # 1 3 5 40 200

変数 $a$b はsortで使うために予約されているので、 sort以外で使わないようにしましょう


配列の関数 sort

「数値」として降順(大きい順)にソートする場合、以下の2つの書き方ができます。

  • sortブロック内の $a と $b を入れ替える方法
my @num = ( 5, 200, 40, 3, 1 );
my @sorted1 = sort { $b <=> $a } @num;
print "@sorted1" . "\n";    # 200, 40, 5, 3, 1
  • sortした結果をreverseで逆順にする方法
my @num = ( 5, 200, 40, 3, 1 );
my @sorted2 = reverse sort { $a <=> $b } @num;
print "@sorted2" . "\n";    # 200, 40, 5, 3, 1

復習問題

  • practice.md
    • 今回の内容を復習することができる問題集です。
    • ご不明な点があれば、気軽にスタッフまでお尋ねください。

練習問題の解答例

  • 2019-answer

    • 各会場の講師が書いた、練習問題の解答例はこちらで公開しています。
    • ※講義終了後に随時追加していきます。
  • 2018-answer

    • 昨年分の解答例はこちらで公開しています。
  • 2017-answer

    • 一昨年分の解答例はこちらで公開しています。

お疲れ様でした!

  • 長い時間、Perl入学式第2回お疲れ様でした!

  • 是非Perl入学式のslackに参加して、サポーターや参加者の皆さんと交流しましょう。

  • 不明点もslackで是非質問してください。

  • 第3回以降の参加もお待ちしております!


次回予告

  • ハッシュ

  • 正規表現

  • リファレンスがないとできないこと


落ち穂拾い


落ち穂拾い

while ループ

my @array = ( 1, "foo", 3 );
my $i = 0;
while ( $i < scalar(@array) ) {
    print "$array[$i]\n";
    $i++;
}
  • while ループを使うことで、 for ループと同じような繰り返し処理が可能です。

  • whileの()の中には、繰り返しを行うための条件式を書きます。

  • 条件式が「真」である限り、whileループは動き続けます。


落ち穂拾い

while ループ

forループでもwhileループでも同じ処理は書けますが、それぞれ適している・好まれるケースがあります。

  • forループは配列全てに対して処理を行う時など、繰り返す回数が決まっている場合に好まれる書き方です。

  • whileループは繰り返す回数は決まっていないが、特定の条件が真の間、ずっと処理を繰り返したいときに好まれる書き方です。

  • whileループの条件式を間違えると、応答がなくなる「無限ループ」という状態に陥ります。その時は Ctrl(Control)キーを押しながらCを押すことで強制的に止めることが可能です


落ち穂拾い

qw ショートカット

my @array = qw( Alice Bob Chris );    # ('Alice', 'Bob', 'Chris')

リストの先頭に qw とつけることで、リスト内の要素にクオォーテーションが不要になります。

上記の例では空白文字 (スペース, タブ, 改行など) が区切りとなり、残ったものがリストの要素になって配列 @array に格納されています。


落ち穂拾い

qw ショートカット

改行区切りを利用すれば、このような書き方も可能です。

my @arary = qw(
    Alice
    Bob
);

落ち穂拾い

qw ショートカット

qw! Alice Bob !
qw# Alice Bob #
qw/ Alice Bob /

デリミタ (区切り文字) には任意の記号文字を使えます。これらは下記の表記と等しいものです。

qw( Alice Bob )

落ち穂拾い

qw ショートカットと範囲演算子

my @array = qw(1 .. 50);
print @array;    # 1..50

qwショートカットと範囲演算子を同時に利用することはできません。