ページ 11

perlで複数の文字列をランダムに抽出

Posted: 2011年5月23日(月) 01:49
by yam
以前お世話になったyamと申します。どうぞよろしくお願いします。

あるファイルから、複数の文字列をランダムに抽出したいと思い、いろいろ調べていたところ、
以下のホームページに行き当たりました。

http://chaichan.web.infoseek.co.jp/qa6500/qa6619.htm

このページを参考にすれば、自分がやりたいことができるのではないかと思いました。

私がやりたいことはあるファイルの中に、以下のような「。」で改行された文字列が1000あります。
その中から重複せずに100の文字列を抽出したいです。

------------------------------

おはようございます。
こんにちは。
こんばんは。
さようなら。
今日は、いい天気ですね。
じゃ、また会いましょう。
………

------------------------------


そこで、先のページを参考にしてプログラムを書いてみたのですが、どうしてもいくつかの文字列が重複してしまいます。

よろしければ重複せずに抽出する方法を、お教え頂けないでしょうか。
何卒よろしくお願い致します。

コード:


open IN,"in.txt" or die "file in.txt did not opened/n"; 
@all = <IN>; 
close IN;

open OUT, ">out.txt";

$num = 100;
@line = (); 

while($#line<$num-1){ 
    $a = int(rand($#all+1)); 

    for(@line){
        next if($a == $_)
    } 

    push(@line,$a); 

}


print OUT $_,":",$all[$_] for(@line); 

close OUT;


 

Re: perlで複数の文字列をランダムに抽出

Posted: 2011年5月23日(月) 04:17
by トントン
おはようございます。
トントンです。

コード:

use strict;

open my $in, "<C:/in.txt" or die "not found\n"; 

my @all = <$in>;
my @lines;
my @tmp;
my %rep;

die "input text is insufficient\n" if(@all < 10);

foreach (0..9) {
    my $index = int(rand(@all - 1));
    push(@tmp, $all[$index]);
}

@lines = grep {++$rep{$_} < 2} (@tmp, @tmp);
仕様が一部わからないので下記部分だけ。
>よろしければ重複せずに抽出する方法
参考になれば良いのですが。

# ちなみにnext if($a == $_) って 行番号比較ですか?

Re: perlで複数の文字列をランダムに抽出

Posted: 2011年5月23日(月) 13:14
by yam
トントンさん

こんにちは、yamです。
まずはわざわざお返事頂き、ありがとうございます。

> 仕様が一部わからないので

言語仕様については私もわかりませんでした。
私はPerlも含めプログラムを勉強し始めたばかりなので、理解が浅いことも確かですが、
「$#」などの書き方について疑問を持ちました。そして調べてはみたのですが、
結局のところ、わかりませんでした。

> 下記部分だけ。
>> よろしければ重複せずに抽出する方法

まずはランダムに10の文字列を抽出する方法をお示しくださったのですね。
勉強させて頂きます。ありがとうございました。
(なお、実際に動かせて頂きましたが、確かに重複することなく文字列を抽出することができました。ありがとうございます!)

ただ、上に示させて頂いたホームページで「これはいいなぁ」と思ったのは、行数が指定できる点と、取得した行数を最後に取得している点でした。

このような形で書くことはできないでしょうか。再度ながら、皆様、何卒よろしくお願い致します。

PS
> # ちなみにnext if($a == $_) って 行番号比較ですか?
そうだと思います。そうだと思いますなんて、無責任な表現ですが (^_^; 先ほどのページにもそのように記されておりました。

for(@line){next if($a==$_);} #重複していたら、次のループへ

取得した行が重複しているのが問題でしたので、私もここらあたりをいろいろ変更してはみたのですが、結局のところどうしていいかわからず、皆様に質問させていただきました。

Re: perlで複数の文字列をランダムに抽出

Posted: 2011年5月23日(月) 17:46
by ISLe
yam さんが書きました:for(@line){next if($a==$_);} #重複していたら、次のループへ
ここのnextはwhileの次のループを期待してのことでしょうけど、実際はこのforの次のループに移行するのでまったく意味のないコードになってしまっています。

コード:

while(@line < $num){ # -1不要
    $a = int(rand(@all)); # +1不要

    $c = 0;
    for(@line){
        ++$c if($a == $_); # 重複してたらカウント
    } 
    push(@line, $a) if($c < 1); # カウントされなかったらOK
}
for(@line){ print OUT ($_+1).":".$all[$_]; } # テキストの行数は1からはじまるようにした
こちらはあらかじめ行番号を付けておいて、1行ずつ抜き出す方法で書いてみました。

コード:

$c = 0;
@all = map(++$c.":".$_, @all);
while(@line < $num){ 
    $a = int(rand(@all));
    push(@line, $all[$a]);
    splice(@all, $a, 1);
}
for(@line){ print OUT $_; }
(修正)
print OUT~の箇所の構文エラー(ブロックの付け忘れ)を修正しました。

Re: perlで複数の文字列をランダムに抽出

Posted: 2011年5月23日(月) 19:58
by yam
ISLeさん

yamです。以前もお教え頂きましたが、この度もいろいろお教え頂き、
本当に感謝しております。ありがとうございます。

また詳しい解説も頂いたため、どうしてうまくいかなかったのか、その原因もよくわかりました。

とても勉強になりました。ありがとうございました。
ISLe さんが書きました: ここのnextはwhileの次のループを期待してのことでしょうけど、
実際はこのforの次のループに移行するのでまったく意味のないコードになってしまっています。
なるほど、そうだったのですね。このご指摘、目から鱗が落ちる思いでした!

また、

コード:

    $c = 0;
    for(@line){
        ++$c if($a == $_); # 重複してたらカウント
    } 
    push(@line, $a) if($c < 1); # カウントされなかったらOK
この箇所についても本当に勉強になりました。ありがとうございました。

それに、もう一つお書きくださったスクリプトについても、参考にさせて頂きました。

コード:

$c = 0;
@all = map(++$c.":".$_, @all);
最初にカウンタをリセットしておいて、そして1行ずつ番号を付けるという方法ですね(その後読み込み)。

おっしゃる通り、こんな方法もあることはわかるのですが、ご指摘されるまで全く気が付きませんでした。
(本当に勉強になりました!)

ISLeさんには前回もお助けくださいましたが、今回もお助けくださったこと、本当に感謝しております。
そして本当に良い勉強ができました。ありがとうございました。

yam

PS
どちらのスクリプトも print のところにエラーが出るのですが、
for(@line) をいちばん後ろに持って行くことで回避されました。