親子におすすめの新型プラネタリウムとは?

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

このQ&Aに関連する最新のQ&A

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に関連する人気のQ&A

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

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

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Q数値かどうかの判定方法

$aに代入されているものが数値かどうかを判定するにはどのようにしたらよいのでしょうか?

Aベストアンサー

$a =~ /^[0-9]*$/
上記の場合、*は「直前のパターンの0回以上の繰り返し」の意味なので、0から9がなくても、つまり$aが空でもマッチしてしまいます。
なので、
$a =~ /^[0-9]+$/
としましょう。
(+は「直前のパターンの1回以上の繰り返し」)
また、0-9は\dで表すこともできるので
$a =~ /^\d+$/
と書くこともできます。

Q複数ファイルの読み込みについて

perl初心者です。

あるディレクトリから拡張子がdataであるファイルを全て読み込みたいのですが、方法がわかりません。
cshで書くと
foreach arg (*.data)
コマンド $arg

のようになりますが、perlだと
foreach $arg (@arg){
コマンド $arg

となりますよね?
引数がリストなのでよくわかりません。
そもそもperlではできないのでしょうか?


それともう一点ですが、ファイルオープンするときに
foreachループの中で
open(FILE, "$arg");
とすることは可能ですか?
上の質問と組み合わせて全てのファイルを開いて作業を行いたいので。

説明が下手ですいません。補足しますのでよろしくお願いします。

Aベストアンサー

while(<*.data>)
{
## $_には、*.DATAなファイル名が格納されている。
open(F,"$_"); ##openする。
while(<F>)
{
##読み出された内容が$_に格納されている。
print $_; ##出力してみる。
}
}

というのが最短コーディングです。

QPerlで特定行から特定行までを抜き出したい

皆さんのお知恵をお貸し頂ければ幸いです。

Perlで以下のようなことをしたいと考えています。
例えば、次のようなテキストファイルがあったとします。

example.log
==================================
aaaa
hogehoge
test
okok
perl
script
==================================

上記ファイルを読み込んで、「hogehoge」から「perl」の間に挟まれた行だけ抜き出したいのです。
イメージとしては、読み込んだファイルを配列に入れて、一行づつ読ませ、キーワード「hogehoge」が現れたらそこでフラグを立て、それ以降の行を表示し、キーワード「perl」が現れた時点で表示を止めるという処理になるのかな?と思っています。

このような場合、どういう風にすればいいのでしょうか?
恐れ入りますが、ご教授頂ければ幸いです。

それでは、どうぞよろしくお願い致します。

Aベストアンサー

> 一行づつ読ませ、キーワード「hogehoge」が現れたらそこでフラグを立て、それ以降の行を表示し、キーワード「perl」が現れた時点で表示を止めるという処理になるのかな?と思っています。

それでいいと思いますよ?これをそのままコード化すると、こんな感じでしょうか。(No.1さんのとはちょっと結果が違います。)

open FH, "example.log" or die $!;
$flag = 0;
while ($data = <FH>) {
  chomp $data;
  if  ($data eq "hogehoge") { $flag = 1 }
  elsif ($data eq "perl")    { $flag = 0 }
  elsif ($flag) { print "$data\n" }
}
close FH;

で、もっと略したいPerlな人だとこんな感じ。Perl独特の記法がふんだんに使われているので、勉強するには不向きかもしれませんが^^;

open FH, "example.log" or die $!;
while (<FH>) {
  print if /^hogehoge$/ .. /^perl$/ and !/^(?:hogehoge|perl)$/;
}
close FH;

※インデントに全角空白を使っているので、コピーする場合はタブなどに置換して下さい。

> 一行づつ読ませ、キーワード「hogehoge」が現れたらそこでフラグを立て、それ以降の行を表示し、キーワード「perl」が現れた時点で表示を止めるという処理になるのかな?と思っています。

それでいいと思いますよ?これをそのままコード化すると、こんな感じでしょうか。(No.1さんのとはちょっと結果が違います。)

open FH, "example.log" or die $!;
$flag = 0;
while ($data = <FH>) {
  chomp $data;
  if  ($data eq "hogehoge") { $flag = 1 }
  elsif ($data eq "perl")    { $fl...続きを読む

Qファイルの行数取得

超初心者です。

いま、表計算的なスクリプトを記述しています。

あるファイルの行数を取得する関数ってあるんでしょうか?

ファイルに記述されている数値を足したり引いたりするのですが、forを使っての計算の際にファイルの行数が必要となりました。

Aベストアンサー

Perlにですね。ないはずです。
行数とはファイルに書かれた改行文字の個数ということなので
実際にファイルを全て読み込まないと行数はわかりません。
以下のように色々な方法があると思います

#### 単純な例
$a = 0;
open FD, "<file.txt" || die $!;
while (<FD>) {
$a++;
}
close FD;
print "行数:$a\n";

### 少しマニアックな方法
open FD, "<file.txt" || die $!;
@a = <FD>;
close FD;
print "行数:" . ($#a + 1) + "\n";


### 反則的方法(外部コマンド) ... UNIXの場合
print "行数:" . `wc -l file.txt` . "\n";

Qひとつの命令を複数行に記述

検索してもあまり解説見かけないなぁと思うのですが、(どこが本家かわからないのでとりあえず放置)

VBでいう_に該当するものは何ですか?
ソースが長くなって見にくくなっているので、対処したいのですが。

hoge = "じゅげむじゅげむごごうのすりきれ" _ '←次の行に送る
& "かいじゃりすいぎょのうんらいまつふうらいまつすいぎょうまつ" 

Aベストアンサー

Perlは、VBと異なり行の概念がありません。
VBではステートメント区切り子が存在しないため、改行がステートメントの区切りとして扱われ、例外的につなげるときに「_」を使うわけですが、
Perlの場合はステートメント区切り子セミコロン「;」がステートメントの区切りになっていますので、セミコロンを打たない限り、何行に分かれてもひとつのステートメントとして扱われます。
したがって、回答としては「そのまま改行してOK」です。
上記の例なら、

$hoge = "じゅげむじゅげむごごうのすりきれ" .
"かいじゃりすいぎょのうんらいまつふうらいまつすいぎょうまつ";

という感じです。
当然ながら、文字列中での改行はダメなので、上記のようにいったん「"」を閉じて、文字列結合演算子「.」で接続することに成ります。

Qperlで配列の要素が空なのを知るには?

教えてください。

例えば、perlの次の様な要素が3つある配列で、

@t = ('A',,'C');

2要素目が空で有ることを判別したいのですが、どうしたら良いでしょうか?

Aベストアンサー

'' と比較する.

QUse of uninitialized value ---

初心者です。フォームに文字を入力してもらい、↓
print blockquote(
textfield(
-name => 'die Antwort',
省略----条件にあえば、
my $value = param('die Antwort');#として
if (($value eq $ans || $value eq $ans2) and ($c eq $num)){
「正解」と表示することにしました。すると、うまくいくのですが、
if (($value eq $ans || $value eq $ans2) and ($c eq $num)){
について「Use of uninitialized value ----」とApacheのerror logに書かれてしまいます。このためerror logがすぐに巨大なファイルになってしまいます。これを避ける方法をお教えください。よろしく、お願いいたします。

Aベストアンサー

コードに use warnings が入っているのですよね。
デバッグも済んでいるのでしたら、
no warnings qw ( uninitialized );
で、この警告の表示を抑制してもよいと思います。

QPerlで変数を使用した置換ができない

ファイル内の「1 2 3 4 5」などの数字の箇所を、「1回 2回 3回 4回 5回」のように、「回」をつけた形に変えることを考えています。
以下のようなスクリプトを書きましたが、うまくいきません。

# 「$search_replace」の参照先のテキストファイルの内容
(\d) タブ記号 $1回


EOF

# 問題のスクリプト(一部)
chomp($search_replace);
(my $search, my $replace) = split(/\t/, $search_replace);
$string[$i+1] =~ s/$search/$replace/g; #ここが問題と思われる箇所

ほしい結果は右のとおり。「1回 2回 3回 4回 5回」
現実の結果は右のとおり。「$1回 $1回 $1回 $1回 $1回」

置換文字列の$1の部分がうまく展開(?)できないようです。
どうすればよいか、ご教示ください。

Aベストアンサー

正規表現の置換部はダブルクォート文字列と同様に評価されるので、次の2つの文は同じように機能します。質問のケースでは、変数を2重に置き換えなければならないので、明示的にダブルクォートが必要になります。

$string[$i+1] =~ s/$search/$1回/g;
$string[$i+1] =~ s/$search/"$1回"/eg;

Q要素を削除する最適な方法

たいへん困っています。Perlでプログラムを作成しており配列要素の削除を行なおうとしています。
例えば下のようなデーターをdata.txtファイルに保存しているとしてください。

1,洋服,婦人服,子供服,男性服,
2,時計,
3,アクセサリ,

上のデーターで1行目にある子供服を削除して
1,洋服,婦人服,男性服,
2,時計,
3,アクセサリ,
のようにずらしたいのです。

私がとった方法はデーターをopenで開きforeachで行をまず呼びこみます。
if(!open(DATA,"$data")){&error('ファイル読み取りエラー。'); }
else{
@data=<DATA>;
close(DATA);
}
foreach $line (@data){
chomp $line;
($no,@sub)=split(/,/,$line);
if($no eq 1){
$i=-1;
foreach (@sub){$i++;
if($sub[$i] eq '子供服'){next;}
else{push @newsub,"$sub[$i]";next; }
}
push @newline,"$no,@newsub\n";}
else{push @newcline,"$line\n";}
}
これで@newlineを表示すると
1,洋服,婦人服, 男性服,
2,時計,
3,アクセサリ,

のように男性の前の部分が半角空白として保存されます。

どうしてなのでしょうか?そもそもこういったやり方が間違っているのか、それとも一部だけまちがっているのかもわかりません。お手数ですがどなたか教えてください。

たいへん困っています。Perlでプログラムを作成しており配列要素の削除を行なおうとしています。
例えば下のようなデーターをdata.txtファイルに保存しているとしてください。

1,洋服,婦人服,子供服,男性服,
2,時計,
3,アクセサリ,

上のデーターで1行目にある子供服を削除して
1,洋服,婦人服,男性服,
2,時計,
3,アクセサリ,
のようにずらしたいのです。

私がとった方法はデーターをopenで開きforeachで行をまず呼びこみます。
if(!open(DATA,"$data")){&error('ファイル読み取りエラー。'); }
e...続きを読む

Aベストアンサー

簡単なことを複雑にやっている印象があります。本当は、bakusuiさんにとって良くないことだと思いますが、私だったら、こうするという例を示します。

$datfile = @ARGV[0];

open(DATA, $datfile) || die "can't open $datfile\n";
while($line = <DATA>){
($num, @items) = split(/,/, $line);
if($num == 1){
$line =~ s/子供服\s*,//g;
}
print $line;
}

Qperlにて2つのファイル比較

いつもお世話になっています。
今回perlで2つのCSVファイルを比較して、マッチしたものをエクセルで表示させるスクリプトを作ろうとしています。
エクセルに表示するときに、OLE32を使うことは前回教えていただいたのですが、2つのファイルを比較する方法が分かりません;
ネットなどでは調べたのですが、参考になるサイトなど見つけられなかったので、詳しい方お力をお貸しください。
参考になりそうなサイトも教えていただけると、とても助かります。
よろしくお願いします。

Aベストアンサー

>一致した項目だけをエクセルで書き出したいのです

 CSVファイルでは、行のことを「レコード」、その行に含まれるデータ1つ1つを「項目」と呼びますが、その認識でよろしいでしょうか?
 ファイルAとBのレコードの数が一致しており、それぞれ1行ずつ読み込んで項目を順番に比較すればいいのであれば、

open(A, "<fileA");
open(B, "<fileB");
while ( <A> ) {
 @a = split(/,/,$_);
 @b = split(/,/,<B>);
 for($i = 0; $i < @a; $i++ ) {
  if ( $a[$i] eq $b[$i] ) {
   print "$a[$i]\t";
  }
 }
 print "\n";
}

 これでイケるはずです。
 でもこれだとデータの順番とかデタラメになっちゃいますけど、いいんですか?(^_^;
 最終的に何がやりたいのか分からないので、これ以上アドバイスしようがないのですが。


このQ&Aを見た人がよく見るQ&A

人気Q&Aランキング