プロが教える店舗&オフィスのセキュリティ対策術

こんにちは、perlについての質問です。
2つの配列を比較するで調べたところ、"@deff = grep { !{map{$_,1}@array2 }-> {$_}}@array1;"で比較ができると書かれていました。perl初心者ですので、よくわからず、自分なりにこの処理について調べてみましたが、わかりませんでした。この処理の詳細がわかる方いましたら、教えてください。(特にmap($_,1)@array2 の部分がよくわからなかったです)
よろしくお願いします。

A 回答 (3件)

関数型プログラムになれてれば


読めるコードですけど,Perl風味が混じってるので
ちょっとしんどいですね.
リファレンスを知ってるかどうかが重要です.

{map{$_,1}@array2 }-> {$_}

ここが何をしてるのかわかれば,大体OKかな
map{$_,1}@array2
の部分では,@array2の要素を一個じつ$_1にいれて
1とペアにしています.たとえば,@array2=(a,b,c,d)だったら
aを$_にして,a,1 をつくって
bを$_にして,b,1をつくって・・・とやって
(a,1,b1,c,1,d,1)
というリストを作ります.これがmapの機能
これを { } で囲んで
{ (a,1,b1,c,1,d,1) }
というのを作るんだけども,これは
{ a,1,b1,c,1,d,1 }
と同じで,無名のハッシュ(ハッシュのリファレンス)を
作る.つまり
{a=>1, b=>1, c=>1, d=>1}
と同じ.
リストをそのままハッシュに変換できるということと,それを
無名ハッシュのコンストラクタで処理してるところが
かなりPerl風味です.

このハッシュリファレンスに対して
>{map{$_,1}@array2 }-> {$_}
とするんだけども,後ろの$_はgrepが渡してくる
@array1の各要素ですので
@array1=(a,b,d,x)だとすると
a に対しては 1
b に対しては 1
d に対しては 1
x に対しては undef
となります.

これを ! で否定するのだから
a に対しては false
b に対しては false
d に対しては false
x に対しては true
がでてきて
grepではtrueになるものだけがでてくるので
結果として
@deff=(x)となります.

例では,
@array1=(a,b,d,x)
@array2=(a,b,c,d)
なので,これは,

@array1の中にあって
@array2の中にはない要素を引っ張り出す

というのが正しい言い方であって
一概に「配列を比較する」といいきれるかは微妙です.
そもそも「配列を比較する」ということ自体が
どういう意味かが明確ではないということにも注意が必要です.
    • good
    • 0

> "@deff = grep { !{map{$_,1}@array2 }-> {$_}}@array1;"で比較ができると書かれていました。


もちろんこういうやり方もアリなのですが、初心者の場合後から見た場合に意味が分かる事が重要だと思うので(理解するまでに結構時間がかかった...)次のようなサブルーチンを書いてみました。
サブルーチンにするとどうしても渡された値が正しいかチェックしなければならないため冗長なコードになりますが...


sub eq_array (&;$$) {
my ($pred, $array1, $array2);

if (ref $_[0] eq 'CODE') {
# もしも最初が{ ... }だった場合
($pred, $array1, $array2) = @_;
} else {
# 普通に配列のリファレンス二つが渡された場合
($array1, $array2) = @_;
}

# 配列の数が同じかチェック
if (@$array1 != @$array2) {
return 0;
}

for (my $i = 0; $i < @$array2; $i++) {
if (defined $pred) {
# 最初が{ ... }だった場合
local ($a, $b) = ($array1->[$i], $array2->[$i]);
if (! $pred->()) {
return 0;
}
} else {
# デフォルトは文字で比較 (sortと同じ)
if ($array1->[$i] ne $array2->[$i]) {
return 0;
}
}
}
# 最後まで一緒だったら真
return 1;
}


このサブルーチンはsort演算子に動きを合わせてあります。
使い方は文字で比較する場合、
eq_array \@array1, \@array2
と配列のリファレンスを渡してください。

それぞれの要素について
$a eq $b
で比較されます。


数値で比較する場合はsort演算子のように
eq_array { $a == $b } \@array1, \@array2
としてください。

それぞれの要素について
$a == $b
のように比較されます。


以下このサブルーチンのテストをした時のコードです。


my @array1 = my @array2 = 1 .. 100;

printf "equal?:%d\n", eq_array_str(\@array1, \@array2);
printf "equal?:%d\n", eq_array_num(\@array1, \@array2);
printf "equal?:%d\n", eq_array \@array1, \@array2;
printf "equal?:%d\n", eq_array { $a == $b } \@array1, \@array2;


@array1 = "a" .. "z";
@array2 = reverse @array1;

# use warningsをしていると
# 数値に変換できない文字列を
# ==で比較すると警告を出してくるので
# ここでは数値では比較しない
printf "equal?:%d\n", eq_array_str(\@array1, \@array2);
# printf "equal?:%d\n", eq_array_num(\@array1, \@array2);
printf "equal?:%d\n", eq_array \@array1, \@array2;
# printf "equal?:%d\n", eq_array { $a == $b } \@array1, \@array2;


表示結果:
equal?:1
equal?:1
equal?:1
equal?:1
equal?:0
equal?:0
    • good
    • 0

#!/usr/bin/perl


use warnings;
use strict;

my @array1 = qw(111 222 333 444 555);
my @array2 = qw(222 444);
my @diff;

# オリジナル
@diff = ();
@diff = grep {
!{ map { $_, 1 } @array2 }->{$_}
} @array1;

print "---\n";
print $_, " " foreach @diff;
print "\n";

# map { $_, 1 } @array2 とほぼ同じ働き
my %hash = ( 222, 1, 444, 1);
# 上記は%hash = ( 222 => 1, 444 => 1); と同じ

@diff = ();
@diff = grep { !$hash{$_} } @array1;
print "\n---\n";
print $_, " " foreach @diff;
print "\n";

# @diff = grep { !$hash{$_} } @array1;とやりたいことは同じ
@diff = ();
@diff = grep { !(defined $hash{$_}) } @array1;
print "\n---\n";
print $_, " " foreach @diff;
print "\n";
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています