質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.92%

Python 内包表記

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 1,945

O.K.

score 30

jupyter notebookでとある数列を求めるプログラムを作りました。
for文の中にあるfor文(for j in range(compare.size + 1)...)を内包表記で簡潔に書きたいのですが、
breakの入れ方がわかりません。教えてください。

%matplotlib inline
import math
import matplotlib.pyplot as plt
import numpy as np

pd.options.display.max_columns = None
pd.options.display.notebook_repr_html = True

grundy_ceil = np.array([0])
grundy_ceil_1p = np.array([0])

for n in range(1, 101):
    compare = np.array([])

    compare = np.append(compare, [grundy_ceil[n - i] for i in range(1, math.ceil(n /2 + 1))])

    for j in range(compare.size + 1):
       if not j in compare:
            grundy_ceil = np.append(grundy_ceil, j)
            break

df = pd.DataFrame(grundy_ceil).T
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+1

今回の目的を果たすためであれば、条件を満たす最初のものを取り出すことができるはずです。
こんなやつ。
ここを参考にしています。

from itertools import count
a = [grundy_ceil[n - i] for i in range(1, math.ceil(n /2 + 1))]
j = next(i for i in count() if i not in a)

コードの速さが気になるのであれば、numpy.arrayへのキャストなくすべきでしょう。
また検索があるので、listやnp.arrayではなくsetを使うとだいぶ早くなります。

import math
import pandas as pd
from itertools import count
grundy_ceil = [0]

for n in range(1, 101):
    a = set([grundy_ceil[n - i] for i in range(1, math.ceil(n /2 + 1))])
    grundy_ceil.append(next(i for i in count() if i not in a))

df = pd.DataFrame(grundy_ceil).T

追記:完全に趣味ですが。ここを参考に。
yag1kazさんのlistからの取り出し方も取り入れました。
sliceをリスト内包表記で書くと遅くなるのは盲点でした…

from timeit import timeit
from itertools import count
import math
import pandas as pd
import numpy as np

pd.options.display.max_columns = None
pd.options.display.notebook_repr_html = True

def f0():
    grundy_ceil = np.array([0])
    #grundy_ceil_1p = np.array([0])
    for n in range(1, 101):
        compare = np.array([])
        compare = np.append(compare, [grundy_ceil[n - i] for i in range(1, math.ceil(n /2 + 1))])
        for j in range(compare.size + 1):
           if not j in compare:
                grundy_ceil = np.append(grundy_ceil, j)
                break
    df = pd.DataFrame(grundy_ceil).T
    return df

def f1():
    grundy_ceil = [0]
    for n in range(1, 101):
        a = [grundy_ceil[n - i] for i in range(1, math.ceil(n /2 + 1))]
        j = next(i for i in count() if i not in a)
        grundy_ceil.append(j)
    df = pd.DataFrame(grundy_ceil).T
    return df

def f2():
    grundy_ceil = [0]
    for n in range(1, 101):
        a = np.array([grundy_ceil[n - i] for i in range(1, math.ceil(n /2 + 1))])
        j = next(i for i in count() if i not in a)
        grundy_ceil.append(j)
    df = pd.DataFrame(grundy_ceil).T
    return df

def f3():
    grundy_ceil = [0]
    for n in range(1, 101):
        a = set([grundy_ceil[n - i] for i in range(1, math.ceil(n /2 + 1))])
        j = next(i for i in count() if i not in a)
        grundy_ceil.append(j)
    df = pd.DataFrame(grundy_ceil).T
    return df

def f4():
    grundy_ceil = [0]
    for n in range(1, 101):
        a = set(grundy_ceil[:-math.ceil(n/2+1):-1])
        j = next(i for i in count() if i not in a)
        grundy_ceil.append(j)
    df = pd.DataFrame(grundy_ceil).T
    return df

a = f0()
b = f1()
c = f2()
d = f3()
e = f4()
print('f1', np.allclose(a[0].values, b[0].values))
print('f2', np.allclose(a[0].values, c[0].values))
print('f3', np.allclose(a[0].values, d[0].values))
print('f4', np.allclose(a[0].values, e[0].values))
'''
一応同じ結果になることを確認
f1 True
f2 True
f3 True
f4 True
'''
n = 1000
print('Original  {}'.format(timeit(f0, number=n)))
print('list      {}'.format(timeit(f1, number=n)))
print('np.array  {}'.format(timeit(f2, number=n)))
print('set       {}'.format(timeit(f3, number=n)))
print('slice+set {}'.format(timeit(f4, number=n)))
'''
Original  9.65767323700129
list      1.1692692330107093
np.array  7.38920716199209
set       0.9272018460032996
slice+set 0.6420557640085462
'''

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/17 18:36

    回答ありがとうございます。
    setを使うというのは盲点でした。

    キャンセル

+1

可能です。リスト内包表記だけをつかって同じように動作するコードが以下です。

import math
import numpy as np
import pandas as pd

b = np.array([0])
for n in range(1, 101):
    a = np.array([])
    a = b[:-(math.ceil(n/2)+1):-1]
    b = np.append(b, [x for x in range(len(a)+1) if x not in a])
df = pd.DataFrame(b).T

ただし、上記のリスト内包表記は元のコードよりも実行速度が10~20%ほど遅いです。

速度重視ならmkgreiさんのコードと上記を組み合わせた以下のコードのほうが10~20%ほど速いようです。

import math
import numpy as np
import pandas as pd
from itertools import count

b = [0]
for n in range(1, 101):
    a = b[:-(math.ceil(n/2)+1):-1]
    b.append(next(i for i in count() if i not in a))
df = pd.DataFrame(b).T

101ぐらいでは実感できないですが1001を超えてくると差がはっきりとわかります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/17 18:57 編集

    a = b[:-(math.ceil(n/2)+1):-1]

    で処理している内容は

    『配列bの要素を、後ろから数えてmath.ceil(n/2)個までの要素を、逆順に取り出してaに代入せよ』

    ということになります。
    後ろから数えてX個目までを取り出す処理は、

    list[:-X+1:-1]

    と書くことで実現できますのでそれを利用しています。

    蛇足)
    括弧を余分につけて
    list[:-(X+1):-1]
    と書いてもおなじですが、こう書くとサルが2匹寝転んで縦につながっているみたいでちょっと面白いですねw

    キャンセル

  • 2017/11/17 19:08

    その視点は遊び心があっていいですね。←蛇足について

    キャンセル

  • 2017/11/17 19:19

    なるほどわかりました。
    ありがとうございます。

    キャンセル

checkベストアンサー

0

Python内包表記はbreak(ループ中断)をサポートしません。(for文では何か問題があるのでしょうか?)

一応、禁じ手的なハック も存在するようですが、全くおすすめしません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/17 12:37

    そうでしたか。ありがとうございます。
    できるだけ短く書きたかったので。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る