Quantcast
Browsing Latest Articles All 20 Live
Mark channel Not-Safe-For-Work? (0 votes)
Are you the publisher? or about this channel.
No ratings yet.
Articles:

jQueryのeachメソッド

HTML要素に対する繰り返し

<ul>
  <li>Ruby</li>
  <li>JavaScript</li>
  <li>PHP</li>
</ul>
  $('li').each(function(index, element){
    console.log(index + ':' + $(element).text());
  })

以下のように出力される。

0:Ruby
1:JavaScript
2:PHP

配列・オブジェクトに対する繰り返し

var array = ['Rails', 'jQuery', 'FuelPHP'];

$.each(array, function(index, value){
  console.log(index + ':' + value);
})

以下のように出力される。

0:Rails
1:jQuery
2:FuelPHP

参考

【jQuery入門】2種類のeach()による繰り返し処理まとめ!

【jQuery】複数の要素に対して繰り返し処理をする(each・for)!

Ruby初心者課題 繰り返し処理

課題を解いて学んだことを書いていきます:no_mouth:

Q ターミナルに、1~10を順に表示してください。
  ただし、5のみ表示させないでください。

解答↓

qiita.rb
num=0
while num<10 do
    num+=1
    next if num==5

    puts num
end

繰り返しの処理を行ってくれるwhile構文を使う

qiita.rb
while 条件式 do
〜処理〜
end

条件式を実行した結果がtrueであれば、do ~ end を繰り返し実行しますが、falseであれば、繰り返しの最後になり、繰り返しを抜け、終了します。

while構文を使うと今回の場合num<10と定義しているので1~10の値がターミナルに表示されます。
ですが、5を表示しないという問題も課されているので5をスキップするために使うのがnextです。

nextは指定の回数の時だけ繰り返しをスキップしたい時に使用します。
スキップした後は、whileの次の回から繰り返し処理が継続されます。

qiita.rb
next if num==5

(もしnumが5だったらスキップ処理をしてください。って感じでかきました。)

ついでに

今回使ったnextと同じ要領で使えるbreakというものがあります。
breakは、繰り返し処理を中断し繰り返しを抜けて終了するために使用します。

qiita.rb
num=0
while num<10 do
    num+=1
    break if num==5

    puts num
end

(numが5の回数の時、繰り返しを抜けて中断して、終了処理をしてください。って感じです。)

この時ターミナルには1~4の数字が表示されている。

【Ruby】じゃんけんアプリを作る(戻り値・while文など・・・)

教えてもらって、やっと理解できたので備忘録として。

janken.rb
def janken
    puts "[0]:グー\n[1]:チョキ\n[2]:パー"
    player_hand = gets.to_i

    program_hand = rand(3)

    jankens = ["グー", "チョキ", "パー"]

    puts "あなたの手:#{jankens[player_hand]}, わたしの手:#{jankens[program_hand]}"

    if player_hand == program_hand
      puts "あいこで"
      return true
    elsif (player_hand == 0 && program_hand == 1) || (player_hand == 1 && program_hand == 2) || (player_hand == 2 && program_hand == 0)
      puts "あなたの勝ちです"
      return false
    else
      puts "あなたの負けです"
      return false
    end
  end

  next_game = true

  puts "最初はグー、じゃんけん..."

  while next_game do
    next_game = janken
  end

必要な予備知識

乱数の作り方

乱数を使うとプログラムの出す手をランダムに定義することができる
randメソッド使うと手軽に乱数を生成できる。

randメソッドに引数として数を渡すと、「0」~「渡した数-1」の間でランダムに数を渡す

rand(欲しい乱数の数)

rand(100)
# => 0~99の中から1つの数字をランダムに返す

改行コード

\n(OSがWindowsの場合は「\」部分が「¥」)
※ダブルクオーテーション「"」で囲んだ場合のみ有効。シングルクオーテーション「`」で囲んじゃダメ

gets または gets.chomp

入力待ち状態。入力されるまでプログラムは動かない。
「変数 = gets」とすることで、エンターキーを押すと入力された値を変数に代入することができる。
getsメソッドはキーボードで入力した文字列を取得する。

数値を取得する場合:gets.to_i (またはgets.chomp.to_i)
→入力された内容を数値に変換し、計算などにも使える

※gets と gets.chompの違い→改行されるかされないかの違い
gets:改行される
gets.chomp:改行されない

【参考】https://qiita.com/Take-st/items/26f29a6dea622d1e7e8d

while文(繰り返し処理)

while文
while 条件式 do # doは省略可
  実行する処理
end

条件式→true/falseに変わるもの。実行したらtrueかfalseになる
・false→falseかnil(何もない状態)
・上記以外はすべてtrueになる

じゃんけんアプリを作る際のポイント

①自分と相手のじゃんけんの手をつくる

この部分

janken.rb
puts "最初はグー、じゃんけん..."
  puts "[0]:グー\n[1]:チョキ\n[2]:パー"
  player_hand = gets.to_i #自分の手の数値を入力 Point.1

  program_hand = rand(3) #0~2の数値の間でランダムに数値が返される Point.2 

  jankens = ["グー", "チョキ", "パー"]

  puts "あなたの手:#{jankens[player_hand]}, わたしの手:#{jankens[program_hand]}" #Point.1、2の数値に対応する文字(グー、チョキ、パーのいずれか)が入る

じゃんけんをする自分の手と相手の手を作る。

【自分の手】0〜2までの手をターミナルから入力する
【相手の手】乱数で0〜2までの数値を生成する

ここで重要なのは今回じゃんけんの手を数値(0〜2)で扱うということ。グー、チョキ、パーという文字列ではない。これはプログラムでは数値のほうが扱いやすく、比較なども簡単なため。

②勝ち、負け、あいこの条件を作る

前提:今回は条件の順番は特に関係ないので、簡単な条件を先に書いていく

条件分岐の注意点:条件は必要な数だけ並べることができるが、複数の条件に合致したとしても、実行されるのは最初に合致した条件の処理のみ。そのため条件の順番を間違えると、期待した分岐がなされないことがあるので条件を書く順番には気を付ける。(今回は順番関係ないので特に気にしなくてよし)

janken.rb
if player_hand == program_hand #あいこの条件
      puts "あいこで"

    elsif (player_hand == 0 && program_hand == 1) || (player_hand == 1 && program_hand == 2) || (player_hand == 2 && program_hand == 0) #勝ちの条件
      puts "あなたの勝ちです"

    else
      puts "あなたの負けです"

    end

#勝ちの条件は下記のようにも書けるが、イメージがつきにくい条件式になるので上記のパターンのが〇
player_hand - program_hand == -1 || player_hand - program_hand == 2

何個かの条件を組み合わせる場合は、
(条件式) || (条件式) || (条件式)のように書けばOK

また、「勝ちの条件式」を別のメソッドに逃がしてすっきりさせる方法もある

janken.rb
def win?(player_hand , program_hand)
   (player_hand == 0 && program_hand == 1) || (player_hand == 1 && program_hand == 2) || (player_hand == 2 && program_hand == 0)
end

if player_hand == program_hand #あいこの条件
      puts "あいこで"

    elsif win?(player_hand , program_hand) #勝ちの条件
      puts "あなたの勝ちです"

    else
      puts "あなたの負けです"

    end

または、次のように変数に代入して逃がす場合でもOK

janken.rb
win = (player_hand == 0 && program_hand == 1) || (player_hand == 1 && program_hand == 2) || (player_hand == 2 && program_hand == 0)

if player_hand == program_hand #あいこの条件
      puts "あいこで"

    elsif win #勝ちの条件
      puts "あなたの勝ちです"

    else
      puts "あなたの負けです"

    end

③あいこの時にもう1度じゃんけんをさせる

・勝ち、負けの場合→プログラムを終了
・あいこの場合→じゃんけんを繰り返す。決着がつく(勝つか負けるか)までじゃんけんを続けるようにプログラムを作る

janken.rb
def janken # jankenメソッドの中でじゃんけん中の処理を書く
    puts "[0]:グー\n[1]:チョキ\n[2]:パー"
    player_hand = gets.to_i

    program_hand = rand(3)

    jankens = ["グー", "チョキ", "パー"]

    puts "あなたの手:#{jankens[player_hand]}, わたしの手:#{jankens[program_hand]}"

    if player_hand == program_hand
      puts "あいこで"
      return true # あいこの場合、trueを返す(ここは true のみでもOK)
    elsif (player_hand == 0 && program_hand == 1) || (player_hand == 1 && program_hand == 2) || (player_hand == 2 && program_hand == 0)
      puts "あなたの勝ちです"
      return false # 勝ちの場合、falseを返す(ここは false のみでもOK)
    else
      puts "あなたの負けです"
      return false # 負けの場合、falseを返す(ここは false のみでもOK)
    end
  end # jankenメソッド終わり

  next_game = true # 1回目を実行するためにtrueをnext_gameに代入

  puts "最初はグー、じゃんけん..."

  while next_game do # next_gameがtrue(あいこ)の間は繰り返し。false(勝ち・負け)になれば繰り返し終了
    next_game = janken # jankenメソッドの返り値(true/false)がnext_gameに代入される
  end

・じゃんけん用のメソッドを定義する(janken)。この中は①、②で部分的に作っていた「じゃんけんの内容」。
そして、jankenメソッド内にまだ次のゲームがあるか、すなわちあいこかどうかをtrueかfalseで返す。あいこならtrue、決着がついたらfalse。trueになれば、繰り返し処理が実行されるようにする。

・while文では、まず1回目のじゃんけんは行われるためにnext_game = true を代入する。
2回目以降は、jankenメソッド内のtrueかfalseがnext_gameに代入される。
falseが代入されればそこで終了、trueが代入されれば同じことが繰り返される。

N重ループの書き方

はじめに

 本記事はプログラミング初心者向けにfor文の入れ子構造を簡潔に書く方法を紹介するものです。競技プログラミングを念頭に置きながら書いていますが、競技プログラミングに限らず様々なコードを書く場面で役に立つテクニックだと思うので是非覚えておいてください。
 初心者にありがちな良くない書き方として過度に深いネスト構造があります。具体例として、ABC080のC問題を解く際、for文の入れ子を10回書いても間違いではありませんがコードの書き方として望ましくないのは明らかでしょう。
ABC080C問題
この書き方の問題は主に二つで、一つ目はコードを書くにしても読むにしてもとても辛いという点、二つ目はN重ループのNの値がコンパイルの時点で確定していなければ困ってしまう点です。個人の感覚にもよるところですがfor文の入れ子は3重が限度だと思います。それ以上にfor文を繰り返したい場合、簡潔なコードを書くための工夫を考えるべきでしょう。

問題設定

 今回は「5桁の4進数を00000から33333まで順に出力する」という問題を通して多重for文の書き方を二つ紹介します。

いけないやり方

for文の入れ子を5回繰り返します

ダメな例
include<iostream>
using namespace std;

typedef long long LL;

//ここからメイン
int main(void) {
    LL i, j, k, n, m;

    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            for (k = 0; k < 4; k++) {
                for (n = 0; n < 4; n++) {
                    for (m = 0; m < 4; m++) {
                        cout << i << j << k << n << m << endl;
                    }
                }
            }
        }
    }

    return 0;
}

正しい出力は得られますが桁数が増えると大変です。

やり方1

n進数を管理するクラスを使って繰り返しを行います

やり方1
#include<iostream>
#include<vector>
#include<string>

using namespace std;

typedef long long LL;
typedef vector<LL> VLL;


//n進数を管理するクラス
class N_Number {
public:
    N_Number(LL n, LL keta) {
        this->N_Shinsuu = n;

        VLL temp(keta, 0);
        this->numbers = temp;

    }

    //数を足す
    void plus(LL a) {
        this->numbers[0] += a;
        LL size = this->numbers.size();
        for (LL i = 0; i < size; i++) {
            if (i + 1 < size) {
                this->numbers[i + 1] += this->numbers[i] / this->N_Shinsuu;
            }
            this->numbers[i] %= this->N_Shinsuu;
        }
    }

    //全ての桁が同じ数字になっていればその数字を返す。それ以外の場合は -1 を返す
    LL check() {
        LL a = this->numbers[0];

        for (LL i = 0; i < this->numbers.size(); i++) {
            if (this->numbers[i] != a)return -1;
        }

        return a;
    }

    LL getNumber(LL keta) {
        return this->numbers[keta];
    }

    LL getKeta() {
        return this->numbers.size();
    }

    LL getShinsuu() {
        return this->N_Shinsuu;
    }

    void setNumber(LL keta, LL number) {
        if (0 <= number && number < this->getShinsuu()) {
            if (0 <= keta && keta < this->getKeta()) {
                this->numbers[keta] = number;
                return;
            }
        }

        cout << "er" << endl;
    }

    void setAllNumbers(LL number) {
        LL size = this->getKeta(), i;
        for (i = 0; i < size; i++) {
            this->setNumber(i, number);
        }
    }

private:
    VLL numbers;
    LL N_Shinsuu;
};

//ここからメイン
int main(void) {
    N_Number i(4, 5);

    while (true)
    {
        string s;
        for (LL j = i.getKeta() - 1; j >= 0; j--) {
            s += to_string(i.getNumber(j));
        }

        cout << s << endl;

        if (i.check() == i.getShinsuu() - 1)break;
        i.plus(1);
    }

    return 0;
}

ネストが深くなりませんし桁数が変わっても簡単に対応できます。

やり方2

再帰を使います。

やり方2
#include<iostream>
#include<vector>
#include<string>

using namespace std;

typedef long long LL;
typedef vector<LL> VLL;

//再帰で繰り返しを行う
void ForCout(LL n, LL keta, VLL numbers) {
    if (numbers.size() == keta) {
        string s;
        for (LL i = 0; i < numbers.size(); i++) {
            s += to_string(numbers[i]);
        }
        cout << s << endl;
        return;
    }

    for (LL i = 0; i < n; i++) {
        VLL temp = numbers;
        temp.push_back(i);
        ForCout(n, keta, temp);
    }
}

//ここからメイン
int main(void) {

    VLL numbers;
    ForCout(4, 5, numbers);

    return 0;
}

再帰は少し難しいかもしれませんが便利な概念なので練習しましょう。

さいごに

 今回はネストが深くなりすぎない多重ループの書き方を紹介しました。最初に紹介した問題とは別に今回の内容を練習できる問題を一つ記事の最後に置いておきますので、一度自分で書いてみることをお勧めします。また、間違いや直した方がいい点がありましたら指摘して頂けると幸いです。
ABC119C問題

【each文の入れ子の挙動】eachの中にeachの中にeach

eachの中にeachを入れるとどういう挙動をするのだろうか。やってみました

なんでeachの中にeachを入れてみたくなったのかというと

スクリーンショット 2019-05-29 21.06.57.png

こんな感じで階層のカテゴリ機能を作成する過程で必要と思いました。

・・・というのも

スクリーンショット 2019-06-01 20.07.45.png

中身がこんな感じでえらいことになっていて
カテゴリの数は全部で400近く・・・

流石に手書きはありえんなと思い
eachでdbから引っ張ってきた要素文回せばいけそう!?

という考えに至った。

スクリーンショット 2019-06-01 20.11.34.png

こんな感じで中でぐるぐる同じ繰り返しが行われるので
安直な考えでeachの中にeachを回して見ようと思った。

【結論】eachの入れ子ってできたっけ?どんな挙動するんだっけ

実験のコードはこちらです↓

test.rb
parents = [1,2,3]
childs = [10,11,12]
babys = [20,21,23]

parents.each do |parent|
  puts "親#{parent}"
  childs.each do |child|
    puts "子#{child}"
    babys.each do |baby|
      puts "孫#{baby}"
    end
  end
end

結果はこちら↓
スクリーンショット 2019-06-01 20.20.36.png

です。

最後に・・・

まだhtmlの方は作り上げてないので
この挙動で本当にいけるのか?という感じですが
今のところこれくらいしかアイデアがないので書きながら探っていきます。

こうしたらいいよ!
こうしたらうまくいくよ!楽チンだよ!
先輩方の知恵があれば教えていただきたいです。

最後まで読んでいただきありがとうございました!

【Ruby】繰り返し処理にインデックスをつける(each_with_index, with_index)

1.今回のテーマ

Rubyの繰り返し処理(eachやmapメソッド等)を使う際、第二引数を指定することで、インデックスを振ることが出来て便利!というお話です。
インデックスとは、1.2.3.4…という番号のこと。
使用用途はたくさんありますが、例えばランキング一覧作成(1位●●2位▲▲3位■■…等の順位の割り振り)等に便利です。

2.インデックスを降らない場合(通常の繰り返し処理)

本題に入る前に、インデックスを付けない場合の繰り返し処理を復習しておきます。
以下通常のeach文です。

book.rb
@books = Book.all
# Bookモデルの全てのデータをインスタンス変数@booksに代入

@books.each{|book| puts "この本の名前は#{book.name}です"}
# こちら通常のeach文です。@booksをそれぞれbookに代入し、book.nameの値をputsで出力するコードです。
# 以下出力結果の例です。
# => この本の名前はハリーポッターです
# => この本の名前はロードオブザリングです
# => この本の...

3.次にインデックスを付けた場合(each_with_indexメソッド)

book.rb
@books.each_with_index{|book, index| puts "#{index}:この本の名前は#{book.name}です"}
# 第二引数に新しくindexが加わりました。(※引数名はご自身で好きに変えてOK)
# 以下出力結果の例です。
# => 0:この本の名前はハリーポッターです
# => 1:この本の名前はロードオブザリングです
# => 2:この本の名前はワンピースです
# => 3:この本の名前はキングダムです

出力結果を見てみると、処理された順に0から番号がふられていることがお分かり頂けるかと思います!
でもインデックスの番号が0から始まってしまっています。
通常1から番号を付けることが多いと思うのですが、これではいちいち+1しなければいけなくて不便です。
でも安心してください。
実は番号を1からふりたい!という場合もメソッドが用意されています。

4.インデックスを指定の数字で始める場合(each.with_index(n)) ※n=数字

book.rb
@books.each.with_index(1){|book, index| puts "#{index}:この本の名前は#{book.name}です"}
# => 1:この本の名前はハリーポッターです
# => 2:この本の名前はロードオブザリングです

先ほどと違う点は2点
①each_with_indexではなくeach.with_index
②each.with_indexの後に(1)で、初期値を1に指定。(100)にすると100から始まります。

5.他のメソッドにwith_indexを付けることも出来る

eachメソッドを例に出してきましたが、その他の繰り返し処理にも使用できます。
mapメソッドを例に例えると、

animal.rb
animals = [dog, cat, rabbit]
animals.map.with_index(1){|animal, i| "#{i}:#{animal}"}
# 以下出力結果の例です。
# => 1:dog
# => 2:cat
# => 3:rabbit

便利ですね!しかし2章で説明したようなmap_with_indexという書き方はこの場合できません!
each_with_indexと書けるのはeachメソッドのみの特権です。
それ以外の繰り返し処理で使用する場合は、with_indexを使用しましょう。

ぜひ活用してみてください:grin:
ご覧頂きありがとうございました!

PHPの繰り返し処理大全

PHP7.3時代の話です。
PHP8や9のころには、また別の結論になっているかもしれません。

最初に結論

・全要素繰り返しはforeach
・途中で打ち切るのはwhile/for
・それ以外はいらん

繰り返し処理一覧

foreach

PHPのforeachは非常に優秀です。
あらゆる反復可能な値を繰り返し処理することができます。

$arr = [1, 2, 3];
foreach($arr as $val){
  echo $val; // 順に1, 2, 3
}

PHPの配列は順序付きリストなので、順番も保証されます。
また、キーと値を両方とも取れるので、inとofどっちだったっけとか悩む必要もありません。

$arr = [3 => 1, 2 => 2, 1 => 3];
foreach ($arr as $key => $val) {
    echo $key, $val; // 31, 22, 13 順番が変わったりはしない
}

オブジェクトにも使えます。

class Test{
    public $a = 1;
    protected $b = 2;
    private $c = 3;

    public function loop(){
        foreach($this as $val){
            echo $val;
        }
    }
}

$test = new Test();
foreach($test as $val){
    echo $val; // 1
}

$test->loop(); // 1,2,3

オブジェクトに使った場合、可視なプロパティが順にアクセスされます。
すなわち外部からはpublicプロパティしか見えず、内部からならprotectedやprivateも見えるということです。

さらに他言語では御法度の、ループ中で自身や別要素を削除するという芸当がPHPでは可能です。

$array = range(0, 10);

foreach ($array as $k => $v) {
    unset($array[$k - 1]);
}

var_dump($array); // [10=>10]

ループ中で、前回ループの値を削除しています。
他言語でこんなことをやるとどんな動作になるかわかったものではありませんが、PHPでは全要素をきちんとループした上で、最終的に$arrayの値は想定通りになります。
配列をforeachした時点で配列のコピーが作成されるので、ループ内で変なことをしでかしてもループ自体には影響が及ばないように対策されているのです。
おかげで何も考えずにfilterが実装できます。

$array = [1, 2, 3, 4, 5, 6];

// 偶数以外削除
foreach ($array as $k => $v) {
    if ($v % 2) {
        unset($array[$k]);
    }
}

var_dump($array); // [2, 4, 6]

while

条件がtrueっぽい値であるかぎり、ループを繰り返します。

$i = 1;
while ($i <= 10) {
    echo $i;
    $i++;
}

foreachとの使い分けですが、foreachは配列などの全要素にアクセスする用途に使います。
whileは何らかの条件でループを脱出する用途に使います。

// 30回ループしたら脱出
$i = 0;
while ($i < 30) {
    echo $i;
    $i++;
}

// 30秒経ったら中断
while(true){
    sleep(1);

    if(time() - $_SERVER['REQUEST_TIME'] > 30){
        break;
    }
}

// 標準入力を出力
while( ($line = fgets(STDIN)) !== false){
    echo $line;
}

うっかり使うと無限ループになるので使用は避けましょう
いや嘘。
whileを使うべきところにforやforeachを使ったりはせず、適切に使い分けましょう。

for

whileで多くの場合必要となる『初期値設定』『カウント処理』を最初から文法に組み込んだものです。

// 30回ループしたら脱出
for ($i = 0; $i < 30; $i++) {
    echo $i;
}

whileではループ前後に書かざるをえなかった定型処理がひとつの文にまとまったことで、わかりやすくなって見た目もすっきりしました。

whileは常にforで書き換え可能なので、実はwhileは必ずしも存在する必要はありません。
しかし初期値設定などが必要ない簡易的なループはwhileで書いた方がわかりやすいので、forとwhileは処理内容によって使い分けるとよいでしょう。

// forを使うほどではない
for(; true; ){
    sleep(1);

    if(time() - $_SERVER['REQUEST_TIME'] > 30){
        break;
    }
}

PHPでは、配列要素などにアクセスする用途でforを使用する理由はありません。

$array = [1, 2, 3];

for ($key = 0; $key < count($array); $key++) {
    echo $key, $array[$key]; // 01, 12, 23
}

$array = [0 => 1, 2 => 3]; // 死
$array = ['answer' => 42]; // 死

歯抜けのない純粋配列にしか使用できず、うっかり連想配列や歯抜けのある配列にforでアクセスすると死にます。
要素に対するアクセスには必ずforeachを使いましょう。

forの存在価値はループのためではなく、条件分岐のためにあります。

for(; time() - $_SERVER['REQUEST_TIME'] <= 30; sleep(1));

条件部分やループカウント部分にあらゆる処理を詰め込むことでforの本文を空にすることもできますが、見づらくなるだけなので止めましょう。

do - while

条件のチェックが最後に行われるということ以外はwhileと同じです。
つまり、たとえ条件が偽でも必ず一回だけは実行されるwhileということです。

$i = 100;
do {
    echo $i++;
} while ($i < 30);

正直、whileではなくこちらを使わなければならないシーンを思いつきません。

関数型関数

PHPには組み込みでarray_walkarray_reducearray_productといった、関数型のように書ける関数が多数用意されています。
これらを使うことで反復処理をやめ、関数型プログラミングっぽい記述にすることが可能になります。

$sum = 0;
foreach($array as $val){
    $sum += $val;
}
echo $sum;

echo array_sum($array); // ↑と同じ

が、基本的にこれらを使う必要はないです。
上記のようなわかりやすい例はむしろ例外で、PHPでは文法の都合上、大抵の処理は関数型で書くと却ってわかりづらくなります。

以下はPHPで高速オシャレな配列操作を求めてより借用した例です。

// 0~10000のうち、偶数だけを抽出して自乗し、結果が20を超えるものを足しあわせよ

// 関数型
echo array_sum(
    array_filter(
        array_map(
            function ($v) {
                return $v ** 2;
            },
            array_filter(range(0, 10000), function ($v) {
                return $v % 2 === 0;
            })
        ),
        function ($v) {
            return $v > 20;
        }
    )
);

// 普通に書く
for ($sum = $v = 0; $v <= 10000; ++$v) {
    if ($v % 2){ continue; }
    $v **= 2;
    if ($v <= 20){ continue; }
    $sum += $v;
}
echo $sum;

明らかに普通に書いた方がわかりやすいですね。
そもそも関数型の例でよく出てくる『○○を抽出する』みたいな処理は、PHPであればSQL発行する時点で絞っておけって話ですし。

filter_varなど使いこなすと色々楽しいこともできるのですが、実用的かと言われると首が傾いてしまいますね。
もちろん関数型で書いてもわかりやすい場合もありますが、別にforeachで書いたところで可読性も大してかわらないので、なら最初から全部foreachで書いた方が手っ取り早いです。

反復処理の定義

オブジェクトに対するループ処理の挙動を、PHPでは任意に定義可能です。
上のほうでオブジェクトをforeachするとpublicプロパティが順番に出てくると言いましたが、それはデフォルトの動作であって、やろうと思えば変更できるということです。

Iteratorインターフェイス

反復処理実装の基本です。
Iteratorインターフェイスをimplementsして各メソッドを実装します。

class Test implements Iterator
{
    public $dummy = '出てこない';
    private $data1 = [1, 2, 3];
    private $data2 = [4, 5, 6];
    private $current = 0;

    /**
     * @Override
     * 現在の値を返す
     * @return mixed 現在の値
     */
    public function current(){
        return $this->data1[$this->current] ?? $this->data2[$this->current - 3] ?? null;
    }

    /**
     * @Override
     * 現在のキーを返す
     * @return string|int 現在のキー
     */
    public function key(){
        return $this->current;
    }

    /**
     * @Override
     * ポインタを次に移動する
     */
    public function next(){
        $this->current++;
    }

    /**
     * @Override
     * ポインタを初期化する
     */
    public function rewind(){
        $this->current = 0;
    }

    /**
     * @Override
     * 現在のポインタが有効か
     * @return boolean 有効ならtrue
     */
    public function valid(){
        return isset($this->data1[$this->current]) ?: isset($this->data2[$this->current - 3]);
    }
}

$test = new Test();
foreach ($test as $key => $val) {
    echo $key, '=>', $val; // [0=>1, 1=>2, 2=>3, 3=>4, 4=>5, 5=>6]
}

publicであるはずの$dummyは出てこなくなり、currentで返した結果が表示されるようになります。
このように、Iteratorインターフェイスを使うことでループで返す値を好き勝手に変更することができるようになります。
ただ、変なことをやってもわかりにくくなるだけなので、基本的にあまり使わないほうがいいと思います。

IteratorAggregateインターフェイス

Iteratorインターフェイスは必ず5個のメソッドを実装する必要があって面倒です。
PHPには最初からイテレータが幾つも用意されており、それらで賄える範囲であれば簡単に実装できるIteratorAggregateがあります。

class Test implements IteratorAggregate
{
    public $dummy = '出てこない';
    private $data1 = [1, 2, 3];
    private $data2 = [4, 5, 6];

    /**
     * @Override
     * イテレータを返す
     * @return Iterator
     */
    public function getIterator(){
        $iter = new AppendIterator();
        $iter->append(new ArrayIterator($this->data1));
        $iter->append(new ArrayIterator($this->data2));
        return $iter;
    }
}

$test = new Test();
foreach ($test as $key => $val) {
    echo $key, '=>', $val; // [0=>1, 1=>2, 2=>3, 0=>4, 1=>5, 2=>6]
}

IteratorAggregate::getIteratorに適当なイテレータを投げれば、foreachループでそれが出てくるようになります。
今回は配列をイテレータにするArrayIterator、複数のイテレータを順にまとめるAppendIteratorを使って、$data1$data2が順に出てくるようにしました。

もちろん、書くのが楽になったからといって使いまくると何が出てくるかわからないブラックボックスになってしまいます。
RecursiveRegexIteratorあたりまで来ると正直何言ってるかわからないので、変なイテレータには手を出さない方がいいと思います。

ジェネレータ

ジェネレータはクロージャとセットにされがちですが、単に何度もreturn(yield)できる関数という認識でいいと思います。

function getPrimeNumber(){
    yield 2;
    yield 3;
    yield 5;
    yield 7;
    yield 11;
    yield 13;
    yield 17;
    return 19; // returnは出ない
}

$primes = getPrimeNumber();
foreach($primes as $prime){
    echo $prime; // 2, 3, 5, 7, 11, 13, 17
}

returnのかわりにyieldというキーワードを使います。
yieldキーワードが入った関数は、関数呼び出しの返り値が自動的にGeneratorインスタンスになります。
たとえ絶対にyieldを通らない実装だったとしてもそうなるので、少し注意が必要です。

返ってくるのはオブジェクトなので、その返り値をforeachでループすることができるわけですが、その際は関数の最初からではなくyieldで止まったところの次の文から処理が再開されます。
関数内の変数値などは維持されるので、何も考えずにメモ化ができたりします。
上で出した例では全く意味がありませんが、無限数列などを作ったりする際にはジェネレータがとても役立ちます。

// フィボナッチ数を求めるジェネレータ
function getFibonacci(){
    $fa = 0;
    yield $fa;
    $fb = 1;
    yield $fb;

    while (true) {
        $fib = $fa + $fb;
        $fa = $fb;
        $fb = $fib;
        yield $fib;
    }
}

$count = 0;
foreach (getFibonacci() as $fibonacci) {
    echo $fibonacci . "\n";
    // 適当に切らないと無限ループする
    if ($count++ > 30) {
        break;
    }
}

再帰もメモ化もgotoもなんもなしに、普通に定義通りのフィボナッチ数を求める関数が書けてしまいました。

ジェネレータは頻繁に出番があるかというと無いですが、覚えておくといざというとき便利な機能です。

まとめ

自発的に使うのはforeachとwhile/forだけでいいよ。

それ以外はあまり出てくるものでもないので、出てきたときに調べるくらいで大丈夫でしょう。

使う必要のないもの

foreachのリファレンス

foreachでリファレンスが取れますが、使用してはいけません。

$array = range(0, 5);
foreach ($array as &$val) {
    $val *= 2;
}
var_dump($array); // [2, 4, 6, 8, 10]

$val = 42;
var_dump($array); // [2, 4, 6, 8, 42] ←

そもそもリファレンスはあらゆる場面で一切使用禁止です。
リファレンスで高速化が云々とか言ってる人は全員間違い1なので、生暖かい目でスルーしましょう。

do - whileの早期return

do-while記法は、簡易的な早期returnに使用できます。
以下はマニュアルに載っている例です。

do {
    if ($i < 5) {
        echo "i は十分大きくはありません。";
        break;
    }
    $i *= $factor;
    if ($i < $minimum_limit) {
        break;
    }
    echo "iはOKです。";

    /* 実際の処理 */

} while (0);

breakはループを抜ける文なので、$i<5だったり$i < $minimum_limitの場合は、このwhileループを抜けます。
条件に当てはまらずループの最後まで来た場合、条件が常に偽であるため、そのままループを終了して先に進みます。
はい、早期returnできました。

もちろんこのような書き方はせず、メソッドなどに出してください。

current / reset / next / prev / end

配列ポインタを手動で操作することが可能です。

$array = range(0, 5);

while(true){
    echo key($array), current($array);
    if(next($array) === false){
        break;
    }
}

配列ポインタ操作関数はresetnextprevendなどひととおり揃っています。
が、あえてこれらの関数を使わなければならない場面はありません。

さらにマニュアルには書かれていないのですが、実はこいつらの引数はZ_PARAM_ARRAY_OR_OBJECT_HTであり、つまりオブジェクトを受け付けます。

class Test{
    public $a = 1;
    protected $b = 2;
    private $c = 3;
}
$array = new Test();

while(true){
    var_dump(key($array), current($array) );
    if(next($array) === false){
        break;
    }
}

01.png

マジかよ。

each

PHP7.2でDeprecatedになったため、使ってはいけません。

いにしえのPHPではメモリ節約のためにeachを使おうなどとされていた時代もありましたが、PHP7時代においては間違った記述です。

上のほうでforeachしたら配列のコピーが作成され云々とか言いましたが実は嘘で、現在ではforeachループするだけならコピーは作成されません。
ループ中で元の配列を変更しようとしたときに初めてコピーが作成されます。
これはコピーオンライトと呼ばれる技術で、最近のPHPの最適化技術のひとつです。
現在のPHPは非常に最適化が進んでいるので、下手なことを考えるより標準機能を使った方がよっぽど使用メモリも少なく速度も速いです。

ジェネレータの変な使い方

素のジェネレータはforeachしかできず、whileやforで書くことができないのですが、実はGeneratorクラスはIteratorインターフェイスをimplementsしているので、手動でループさせることもできます。

$fibonacci = getFibonacci();

while ($fibonacci->key() < 28) {
    $fibonacci->next();
    echo $fibonacci->current(); // 1, 1, 2, 3, 5, 8, …
}

こんな処理が必要になるのであれば、ジェネレータではなく他の機能を使った方がよいでしょう。

yield fromキーワードを使って、別のジェネレータの返り値を取り込むことができます。

function gen()
{
    yield 1;
    yield from [2, 3];
    yield from gen2();
}

function gen2()
{
    yield 4;
    yield 5;
}

$gen = gen();
foreach ($gen as $v) {
    echo $v; // 1, 2, 3, 4, 5
}

こんな処理を作り込むよりも、データ構造を見直した方がよいでしょう。

ソート

usortなんかは関数型っぽい書き方なのでループと言えないこともない可能性がなきにしもあらずな気がしないでもないんだけどループの範疇に含めるべきものだろうか?

$arr = [3, 1, 2];

usort($arr, function ($a, $b) {
    return $a <=> $b;
});

var_dump($arr); // 1, 2, 3

やはりループっぽくはないですね。

その他

思いついたのを並べたらこんなかんじでした。
見落としや間違いがあったら誰かがプルリクしてくれるはず。


  1. 0.1%くらい正しいことを言っている可能性もあるが、見分けは付かないので全て間違いと考えてかまわない。 

初心者のためのVB.NETの基本 ~繰り返し処理編~

はじめに

初心者向けに参考書の0章によくある感じのお話です。
が、教科書では御法度な感覚的表現を多用してます。

なお、VB.NETに限らずプログラミングのイロハ的な内容も含んでいます。

繰り返し処理とは

読んで字のごとく一定の処理をぐるぐると繰り返すだけのことです。ただ繰り返し処理の中にも複数のキーワードが存在するので、一つずつ見ていきましょう!

※文中使用させていただいている各種フローチャートは、こちらより引用させていただきました。

For文

for文.png

For カウンタ変数 = 初期値 To 終了値 (必要ならStep) 
  繰り返す処理
Next カウンタ変数

For文は何回繰り返し処理を行うのか分かっている場合に使用します。

カウンタ変数

繰り返し処理でよく見る"i"のことです。繰り返しで一定間隔ずつ増加する値を表現しています。

上記コードで終了値の脇にStepと書きましたが、ここには増加していくカウンタ変数の増加の値を書きます。ちなみにカウンタ変数はデフォルトでiは1ずつ増加するので、それ以外の増加の際には明示的に"Step 数値"を記述してください。

While文

while文.png

While 条件式
  繰り返す処理
End While

For文では処理を繰り返す回数が決まっていたのに対し、While文ではどういう状態であれば処理を続けるかの条件が必要となります。

While文では処理を実行する前に"継続条件"を読みに行きます。ここで一度目の処理を実行する際にいきなり継続条件を満たしていない場合もあります。このような時While文は問答無用に処理をぶった切ります。
大前提の条件を満たせないなら、処理はさせない!!厳格さが売りのWhile文です。

Do Loop文

do_while.png

While文のフローチャート図とDo Loopのそれとを見比べてみましょう。似たような構図ですが、"文"と"継続条件"の位置関係が入れ替わっているのに気付きましたか?これがWhile文とDo Loop文における違いを生み出しています。

大前提の条件を満たせないなら、処理はさせない!!厳格さが売りのWhile文です。

厳格なWhile文に対して、多少ルーズなのがDo Loop文です。

"継続条件"を読む前に"文"を読みに行っています。これはつまり「こいつは実行していいやつなのか?!」を判断する前にまず最初の処理を実行させてしまうわけです。問題が起きてから捕まえればいいでしょ~って思っちゃうのんきな警備員、といったところでしょうか。

こんな感じのルーズなDo Loop文なので使いどころもWhile文より限定されるようです。

Do While 条件式
  繰り返す処理
Loop

Do
  繰り返す処理
Loop While 条件式

Do Until 条件式
  繰り返す処理
Loop

Do
  繰り返す処理
Loop Until 条件式

ちなみにDo Loop文には上記のように様々なバリエーションが存在します。使い分けについてはまた後日?

Exit(中断)

ここまで見てきた繰り返し処理ですが、原則"継続条件"を満たす限りは処理は続いていきます。当然ですよね。

なんですけど、様々な理由から当初の繰り返しよりも少ない回数や条件で処理を行いたい時もあるかもしれません。そんな時に使うのがExitです!

Dim i As Integer

For i = 0 To 100
  Console.WriteLine(i)
  If i = 4 Then Exit For
Next
'実行結果
0
1
2
3

カウンタ変数が0から100までの間処理を繰り返す(しかもStepだって明示してないってことは1ずつしか増加していかない...)だなんて、気が思いやられる。

と思いきや一瞬で処理が終わってしまいました!

スパっと処理を終了させる感じがReturnと似てますね。実際、プロシージャの処理を中断させる用途でもExitは活躍します。ただし、Returnは戻り値を返すという性質を持ち合わせているので、Exitとの使い分けには注意してくださいね。

Continue(スキップ)

For i As Integer = 0 To 5
  If i = 2 Then Continue For
  Console.WriteLine(i)
Next
'実行結果
1
3
4
5

Continueを使うことで、カウンタ変数が特定の値のときの処理を実行しないで済ますことができます。

Exitの場合、カウンタ変数が宣言した値に到達したら処理そのものが終了していました。それに対し、Continueはその名の通り処理自体は続行していきます。

無限ループ

例えば、Do Loop文で条件式を記述しない場合には、終わり時が判断つかないため無限に実行されてしまいます。これを無限ループといいます。この状況下ではユーザが明示的にプログラムを終了させるまでの間、処理は実行され続けるわけです。

Do
  Console.WriteLine("Hello!")
Loop

※上記のコードを実行すると、一定時間の継続で落ちます。実行するのはあまりおすすめしません

一見やっかいな無限ループですが、広く使用されるテクニックでもあります。
実はメモ帳アプリなんかもこれを応用しています。思い出してほしいんですが、メモ帳って勝手にプログラムを終了することってないんですよね。ユーザが☓ボタンを押すなどして終了させるまで生き長らえます。まさしく無限ループが使われていますね。

なお上記ではDo Loop文での無限ループ例ですが、ほかにもFor文やWhile文でも再現可能です。気になる人はググってみてね。

リファレンス

初心者はもちろん、玄人の皆さんも参照されていること必至のリンク群です。

DOBON.NET
具体的に豊富な単元について解説されています。.NETを書く人なら一度はお世話になっていそうな有名なサイト。

総武ソフトウェア推進所
各単元について深く掘り下げて解説してくれます。いつもお世話になってます!

Microsoft公式
困ったときは公式に当たるのが定石らしい。

VisualBasic中学校VB図解
こちらも単元ごとにていねいに解説されています。

未確認飛行C
VB.NETにフォーカスしていないものの、参考になる部分も多そうです。

PHPの基礎

はじめに

最近、phpの勉強をしています。progateでphpの学習を一通り終えましたが、あまり理解できていなかったので、記事にして、覚えにくかった場所を自分なりにまとめてみようと思います。
基本のきの部分ですので、phpこれから勉強しようかなと思われている方の役に立てばと思います。

配列

sample.php
ノーマルの配列
<?php//$scoresという配列を定義$scores=array('70','90','80');//$colorsの一番目の要素を出力echo$scores[0];//結果、70を出力//$scoresに要素を追加$scores[]='100';//$scoresに追加した要素を出力 echo$scores[3];//結果、100を出力?>連想配列(要素にインデックス以外にキーを持たせる)
<?php//$scoresという連想配列を定義$scores=array('数学'=>'70','英語'=>'90','国語'=>'80');//インデックスではなくキーで要素を出力 echo$scores['英語'];//結果、90を出力//国語(80)に5を足し合わせる$scores['国語']+=5;echo$scores['国語'];//結果、85を出力?>

繰り返し(ループ)処理

for文

sample.php
<?phpfor(初期値を定義;ループの条件;変数の更新){繰り返す処理;}?>例文
<?php//1~100までの数字を出力させる。for($i=1;$i<=100;$i++){echo$i.'<br>';//'<br>'は、改行を意味する}?>

while文

sample.php
<?php初期値を定義;while(ループの条件){繰り返す処理;変数の更新;}?>例文
<?php//2~100までの数字で2の倍数のみを出力させる。$i=2;while($i<=100){echo$i.'<br>';$i+=2;}?>

break文(ループを強制的に中断する)

sample.php
<?php//1~5までの数字を出力させる。for($i=1;$i<=10;$i++){if($i>5){break;//$iの値が6になった時点でループを強制的に中断する命令}echo$i;}?>

continue文(ある条件だけスキップし、ループそのものは継続)

sample.php
<?php//3の倍数以外の数字(100まで)を出力させる。for($i=1;$i<=100;$i++){if($i%3==0){continue;//$iの値が3の倍数になった時だけスキップ ループそのものは、継続}echo$i;}?>

foreach文

sample.php
<?php配列の定義;foreach(配列もしくは連想配列as値変数){繰り返す処理;}?>例文
<?php//配列$dataを定義$data=array('東京','大阪','名古屋');//先頭のデータから順に繰り返し出力させる。foreach($dataas$value){echo$value;//結果、東京、大阪、名古屋を出力}?>

関数

代表的な関数

sample.php
strlen-要素の文字数を数える
<?php//変数$strを定義$str='hello';//変数$strの文字数を出力echostrlen($str);//結果、5を出力?>
count-配列の要素の数を数える
<?php//配列$arrayを定義$array=array('HTML','CSS','PHP');//変数$arrayの要素数を出力echocount($array);//結果、3を出力?>
rand-ランダムな整数を出力
<?php//10~15のランダムな整数を出力echorand(10,15);//結果、12を出力?>

自作の関数

sample.php
シンプルな関数
<?phpfunction$関数名(){処理;}?>例文
<?php//関数hello()を定義functionhello(){echo' Hello, world';}//関数hello()を呼び出すhello();//結果、Hello, worldを出力?>戻り値と引数を用いて
<?phpfunction関数名(引数){return処理;}?>例文
<?php//関数getCircleArea()を定義functiongetCircleArea($radius){return$radius*$radius*3;//今回は、5 * 5 * 3になってます。}//関数getCircleArea()を呼び出し、$circleAreaに代入$circleArea=getCircleArea(5);//$circleAreaを出力echo$circleArea;//結果、75を出力?>

最後に

今回は、構文ばっかり並べてみました。まとめながら書くことは、頭の整理に繋がり、いい感じです。
理解が遅いので早く理解できるようになりたいです。
また、早くLaravel習得してポートフォリオを作成できたらと思います。
この記事を見ておかしいんじゃないのとかありましたらどんどん指摘してください、よろしくお願いします。

結局pythonのforループはどのように書けば早いのか

結局pythonのforループはどのように書けば早いのか

結局pythonのforループはどのように書けばいいのか悩むことが多く、個人的に少し試してみましたので備忘録として書いておきたいと思います。

forループを高速にするための記法としては、「リスト内包表記」が有名ですが、ジェネレータを使った書き方も試してみます。

三重forループを以下4つの書き方を試してみます

  • 愚直にforループを回す
  • リスト内包表記
  • ジェネレータ内包表記
  • 愚直にforループを回しつつyieldで返す(ジェネレータを作る)

比較してみる

愚直にforループを回す

N=300lst=[]forXinrange(N):forYinrange(N):forZinrange(N):lst.append(X+Y+Z)ans=sum(lst)

forループを3つ重ねてみました。
5回実行したときの平均所要時間は6.06[sec]でした。

リスト内包表記

先ほどのコードをリスト内包表記で書いてみます。

N=300tmp_list=[X+Y+ZforXinrange(N)forYinrange(N)forZinrange(N)]ans=sum(tmp_list)

5回実行したときの平均所要時間は4.36[sec]でした。

ジェネレータ内包表記

リストを作る必要はないのでジェネレータ式に変更してみます。リストの[]を()に変更するだけです。

N=300tmp_list=(X+Y+ZforXinrange(N)forYinrange(N)forZinrange(N))ans=sum(tmp_list)

5回実行したときの平均所要時間は3.54[sec]でした。
ジェネレータ式にすると、早くなりますね。

愚直にforループを回しつつyieldで返す(ジェネレータを作る)

内包表記を用いることで記述を簡潔にできますが、処理によっては可読性を損なうことがあります。できればforループを使いつつ、高速化する方法が欲しいところです。
そこで下記のようにジェネレータを定義する関数を用意してみました。

defsample():forXinrange(N):forYinrange(N):forZinrange(N):yieldX+Y+Zans=sum(sample())

5回実行したときの平均所要時間は3.71[sec]でした。

遅くなるかと思いましたが、ジェネレータ内包表記とほとんど差がない実行速度にできました。可読性が悪くなるような内容ならこのようにヘルパー関数のようなものでジェネレータを作ってみるとよさそうです。

まとめ

forループの書き方実行速度(5回の平均)メリットデメリット
愚直にforループを回す6.06可読性がよい遅い
リスト内包表記4.36やや早い可読性を損なう場合がある
ジェネレータ内包表記3.54早い。メモリを節約できる。可読性を損なう場合がある。ジェネレータのため複数回呼び出せない
愚直にforループを回しつつyieldで返す3.71早い。メモリを節約できる。可読性を担保できる。ジェネレータのため複数回呼び出せない。

結論としては、
forループを書く際の基本方針は、
ジェネレータを用いて書く。基本は内包表記で書き、ただし可読性を担保できないようであれば、ジェネレータを関数で定義する。

もちろん、例外はあると思います。

以上です。

あとがき

@intermezzo-frさんの記事 Pythonのリスト内包表記の速度を拝見すると、愚直なforループが遅いことの原因はappendの実行速度にあるそうです。そういう意味では、愚直にforを書いてyieldで返しても内包表記と実行速度がほとんど変わらないのは自然かもしれません。

[Ruby]繰り返し処理のやり直し redo

Rubyで便利な繰り返し処理について、やり直したい時に使える"redo"の使用方法について、まとめてみました。


friends = ['キム','リー','アルフォンソ']
friends.each do |friend|
 print "#{friend}は一番の親友ですか? => "
 rank = ['ベストフレンド','中の上','よっ友'].sample
 puts rank

 #ベストフレンドでなければ、もう一度聞き直し無理やりベストフレンドと言わせる
 redo unless rank == 'ベストフレンド'
end
キムは一番の親友ですか? => 中の上
キムは一番の親友ですか? => よっ友
キムは一番の親友ですか? => 中の上
キムは一番の親友ですか? => ベストフレンド
リーは一番の親友ですか? => 中の上
リーは一番の親友ですか? => ベストフレンド
アルフォンソは一番の親友ですか? => ベストフレンド

上記のように、ベストフレンドというまで、何度も同じ質問が続きます。

無限にやり直しを避けるために、countを使う

friends = ['キム','リー','アルフォンソ']
count = 0
friends.each do |friend|
 print "#{friend}は一番の親友ですか? => "
 # わざとベストフレンドと答えないようにする
 rank = ['中の上','よっ友'].sample
 puts rank


 count += 1
 #やり直しは3回までにする
 redo if rank != 'ベストフレンド' && count < 3

 #カウントをリセット
 count = 0
end
キムは一番の親友ですか? => よっ友
キムは一番の親友ですか? => よっ友
キムは一番の親友ですか? => 中の上
リーは一番の親友ですか? => 中の上
リーは一番の親友ですか? => よっ友
リーは一番の親友ですか? => よっ友
アルフォンソは一番の親友ですか? => 中の上
アルフォンソは一番の親友ですか? => よっ友
アルフォンソは一番の親友ですか? => よっ友

やり直しの回数をcountを使って制限できた。

無限ループに気をつける

friends = ['キム','リー','アルフォンソ']
count = 0
friends.each do |friend|
 print "#{friend}は一番の親友ですか? => "
 # わざとベストフレンドを答えないようにする
 rank = ['中の上','よっ友'].sample
 puts rank


 count += 1
 #やり直しは3回までにする
 redo if rank != 'ベストフレンド' && count < 3

end

redo if rank != 'ベストフレンド' && count < 3行の下に、
"count = 0" を入れ忘れると、無限ループする。

friends = ['キム','リー','アルフォンソ']
friends.each do |friend|
 print "#{friend}は一番の親友ですか? => "
 rank = ['ベストフレンド','中の上','よっ友'].sample
 puts rank

 #firstではなければ、もう一度聞き直し無理やりベストフレンドと言わせる
 redo unless rank == '親友'
end

また、カウント制限なしで、redoを使う際
redo unless rank == '親友' のように配列の要素にない値を指定すると、これまた無限ループするので注意が必要です。
試してみたら、見事に無限ループしました、、、

プロを目指す人のためのRuby入門

[Python]イテラブル、イテレーター、ジェネレーターをざっくり理解する

イテラブル(Iterable)、イテレーター(Iterator)、ジェネレーター(Generator)について使いこなしたいが、正直なんとなくしか分からないのでイメージしやすいように簡単にまとめました。

イテラブル(Iterable)

繰り返しに使えるオブジェクト。list, dict, set, strクラスのオブジェクトはイテラブル。
簡単に言うとfor構文で使えるもの

イテレーター(Iterator)

イテレーターは、イテラブルの一種(イテラブルは、イテレーターとは限らない)
簡単に言うとリストみたいなもの
next()で現在の要素を返し、次の要素に進めます
簡単に言うとリストみたいなものから空になるまで取り出していく

ジェネレーター(Generator)

関数中に[yield 返り値]があると、その関数をジェネレーター関数と呼ぶ。また、リストの内包表記を角括弧から丸括弧に変えたものをジェネレーター式と呼ぶ。
(i for i in range(N))
その関数や式の返り値をジェネレーターイテレーターと呼ぶ。
簡単に言うとイテレーターを返すものです。

イメージ図
スクリーンショット 2020-05-28 14.47.39.png

ジェネレーターの特徴
defsample():cumsum=0foriinrange(1,5):cumsum+=iprint(f'最初にこっち→{cumsum}')yieldcumsumforcumsumingenerator_sample2():#ポイント
print(f'yieldの分→{cumsum}')

<出力結果>
最初にこっち→1
yieldの分→1
最初にこっち→3
yieldの分→3
最初にこっち→6
yieldの分→6
最初にこっち→10
yieldの分→10

イテレーターの特徴1
sample_list=[iforiinrange(1,6)]print(f'リストの中身 {sample_list}')sample_iter=iter(sample_list)#iter()でイテレーターにする
foriinsample_iter:print(i)print(f'リストの中身は変わらない {sample_list}')print(f'イテレーターは使うと空になる {list(sample_iter)}')

<出力結果>
リストの中身 [1, 2, 3, 4, 5]
1
2
3
4
5
リストの中身は変わらない [1, 2, 3, 4, 5]
イテレーターは使うと空になる []

イテレーターの特徴2
sample_list=[iforiinrange(1,6)]print(f'リストの中身 {sample_list}')sample_iter=iter(sample_list)#iter()でイテレーターにする
print(f'リストみたいなもの {sample_iter}')#ここで使ってる
print(f'リストとして見れる {list(sample_iter)}')print(f'イテレーターは使うと空になる {list(sample_iter)}')

<出力結果>
リストの中身 [1, 2, 3, 4, 5]
リストみたいなもの
リストとして見れる [1, 2, 3, 4, 5]
イテレーターは使うと空になる []

<参考>
イテラブルについて
イテレーターについて
ジェネレーターについて

まとめてるうちに何となく理解できた
まだまだざっくりのうちなので、もう少し理解できたらまとめます


追記

@shiracamusさんご指摘ありがとうございます。

Python openpyxlでのコピーシートを最初に表示する方法

Python openpyxlについて

PythonでのExcelのファイルやシートを作成、書き込みのパッケージ
official document (http://openpyxl.readthedocs.io/en/default/)

シートコピー

ファイルのシートのコピーには以下を用いることが多い

workbook.copy_worksheet(worksheet)

from openpyxl import load_workbook

#対象のエクセルファイル指定
xls_file='test.xls'

#openpyxlでエクセルファイル(ブック)とアクティブシートを読込
wb=load_workbook(xls_file)
ws=wb.active

#アクティブシートをコピー
ws_copy=wb.copy_worksheet(ws)

#コピーされたシートのタイトルを変更する
ws_copy.title='新シート名'

#リストで量産するなら
list=['シート1','シート2','シート3','シート4']
for sheet_name in list:
    ws_copy = wb.copy_worksheet(ws)
    ws_copy.title=sheet_name

#保存して終わり
wb.save(xls_file)

コピーシートを最初に表示したい

コビーシートでセルの編集などしてデータをアップデートしたいときがある、最新データを次ファイルアクセスした時に出したい。だが...
新しいビットマップ イメージ.jpg
実際シートをコピーしただけだと最新のコピーシートを最初に表示してくれない。

アクティブシートの存在を知る

アクティブシートとは、エクセルを開いたときに表示される画面のことを言います。

エクセルの開始

[ スタート ] ⇒ [ 全てのプログラム ] ⇒ [ Microsoft Office ] ⇒ [ Microsoft Excel ]

activesheet1.gif

クリックで開始

エクセルが開きました。この状態が「アクティブシート」です。

エクセルには複数のシートがあります。

activesheet2.gif

初期状態ではSheet1から3まで表示されています。「Sheet1」を選択している場合、アクティブシートは「Sheet1」になります。
参考:Excel エクセルマクロ・VBA塾(http://kabu-macro.com/)

解決

作成したコピーシートをアクティブシートにすればいい

#コピーシートをアクティブシートにする
wb.active = wb.sheetnames.index('シート4')

[備忘録] [初心者] Rubyの繰り返し文中で配列要素の値を変更する際の書き方

(ruby 2.5.3)

結論

JavaScriptの繰り返し文について、配列要素に変更を加えながら回したい時、

.js
for(vari=0;i<A.length;i++){// 省略:ある条件の元、A[i]の値を変更する}

同じ雰囲気でRubyで再現するなら、

.rb
A.each_indexdo|i|## 省略:ある条件の元、A[i]の値を変更するend

と書くことにする。

背景

JavaScriptで書いた私的なコードをRubyに書き直していた。
下記JavaScriptの関数はその一部で、英文のパーツを配列で渡すと、末尾の記号によって文頭に当たる語の先頭を大文字に変更して結合して出力するというもの。

Upcasing.js
functionUpcasing(result_lines){result_lines[0]=result_lines[0].replace(/^[a-z]/,result_lines[0].charAt(0).toUpperCase());varbreak_flg=false;constbreak_sign={".":true,"!":true,"?":true,",":false};for(vari=0;i<result_lines.length;i++){if(break_flg===true){result_lines[i]=result_lines[i].replace(/^[a-z]/,result_lines[i].charAt(0).toUpperCase());}if(break_sign[result_lines[i].slice(-1)]===true){break_flg=true;}else{break_flg=false;}}returnresult_lines.join("");}console.log(Upcasing(["oh,","yeah!","hello!"]));// 実行結果// Oh, yeah! Hello! //   <= "hello!"だったものが、直前の"!"によって、次の語の先頭が大文字になっている

そのままRubyのメソッドに書き換えた(と思い込んだ)ものが下記。

(fail)Upcasing.rb
defUpcasing(result_lines)result_lines[0]=result_lines[0].capitalizebreak_flg=falsebreak_sign={"."=>true,"!"=>true,"?"=>true,","=>false}forrinresult_linesifbreak_flg==truer=r.capitalizeendifbreak_sign[r.slice(-1)]==truebreak_flg=trueelsebreak_flg=falseendendjoined_line=result_lines.join(" ")returnjoined_lineendprintUpcasing(["oh,","yeah!","hello!"])## 実行結果## Oh, yeah! hello! <= "hello!"の"h"が大文字になっていない

for式eachメソッドのようにスコープを作成しないのでfor r in result_linesrは、ブロック外でも使用できるが、そもそも元の配列要素とは別に新しく定義される変数であるので、配列要素に変更を加えたいならば、ブロック内でもresult_lines[i]を操作する必要がある。(この変数rはブロックパラメータでは無い。)

普段、Rubyのコードを書くときに、繰り返しのブロック内で値を代入するような場面があまりなかったため、ブロックパラメータのような一時的な箱の概念や、生成されるスコープへの認識が欠けていた。

(※ この備忘録で使用したのはruby 2.5.3です。)

上記コードのようなfor 変数名 in 配列名の形だと、ブロック内で配列要素を指定するためのインデックスが変数に定義されないため、each_indexメソッドを使用して以下のように変更した。
(下記コードは後日投稿の記事内でリファクタリングしました。)

(success)Upcasing.rb
defUpcasing(result_lines)result_lines[0]=result_lines[0].capitalizebreak_flg=falsebreak_sign={"."=>true,"!"=>true,"?"=>true,","=>false}result_lines.each_indexdo|i|ifbreak_flg==trueresult_lines[i]=result_lines[i].capitalizeendifbreak_sign[result_lines[i].slice(-1)]==truebreak_flg=trueelsebreak_flg=falseendendjoined_line=result_lines.join(" ")returnjoined_lineendprintUpcasing(["oh,","yeah!","hello!"])## 実行結果## Oh, yeah! Hello!

(今回については、繰り返しブロック内で定義した変数をその外で使用する用事が無いので、for式eachメソッドかによるスコープの生成有無による全体への影響は生じない。)

所感

変数名を省略して文量を減らせるのはブロックパラメータを利用する大きな利点だと思うが、繰り返し過程での当該の配列要素の変更がありきだと、あまり使うことはないのかもしれない。
下記の没案のようにeach_with_indexメソッドで値もインデックスもブロックパラメータに渡して、現在の要素を参照する部分のみで使用するなども考えたが、書き間違えとかが増えそうな気がして止めた。
変数名が| r |じゃなくて、| current_element |とかならまだ分かり易いかもしれない。

没案.rb
result_lines.each_with_indexdo|current_element,i|ifbreak_flg==trueresult_lines[i]=current_element.capitalizeendifbreak_sign[current_element.slice(-1)]==truebreak_flg=trueelsebreak_flg=falseendend

Ruby | 配列の奇数番目と偶数番目を取り出す方法

配列の奇数番目や偶数番目を取り出して新たな配列にする

配列の中から偶数番目または奇数番目の要素を取り出したいときに役に立ったので共有します。
今回は、each_sliceメソッドとmapメソッドを使用してやってみました。

参考記事
each_slice (Enumerable) - Rubyリファレンス
ある配列を任意の要素数の配列に分割したい - Qiita

結論はこんな感じです。

ary=[1,2,3,4,5,6,7,8,9,10]odd=ary.each_slice(2).map(&:first)even=ary.each_slice(2).map(&:last)podd#=>[1,3,5,7,9]peven#=>[2,4,6,8,10]

少し分解して考えてみた

結論の書き方だと何をやっているのかイマイチ分かりにくいと思いますので、自分なりに分解してみました。

ある配列を任意の要素数の配列に分割したい - Qiita
each_sliceについては、上の記事が分かりやすく解説していました。

each_slice(数): 数の部分には何分割するかを決める数値を入れる

なので、数の部分を2とすると、配列を2つずつ取り出して、それをさらにmapメソッドで配列に入れている感じです。

ary.each_slice(2).map{|n|n}#=>[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]

これを利用して、以下のようにします。

odd=ary.each_slice(2).map{|n|n.first}#[1, 2].firstみたいなことを繰り返している#=>[1,3,5,7,9]even=ary.each_slice(2).map{|n|n.last}#=>[2,4,6,8,10]
  • 取り出した配列nの要素の1番目を取り出すと、奇数番目の配列が完成する。
    つまりfirstメソッドを使用する。
  • 取り出した配列nの要素の2番目を取り出すと、偶数番目の配列が完成する。
    つまりlastメソッドを使用する。



初心者なりに考えたやり方になりますので、他にも色々なやり方がたくさんあると思います。
もしこんなやり方もあるよ!って方がいればぜひ教えていただければと思います。
読んでいただきありがとうございました。

Browsing Latest Articles All 20 Live