8

ストーリー:生成リスト

:現在

、私は整数のリストを以下のルールでのリストを期待する関数、被試験を持っています

  1. サブリストの数(のはNそれを呼び出すせて)1から50
  2. にすることができるサブリスト内の値の数は、全てのサブリスト(矩形形状)についても同様であり、> = 0と< = 5であるべきです
  3. サブリスト内の値は、サブリストの合計数以下にすることはできません。

    [[0]] 
    [[2, 1], [2, 0], [3, 1], [1, 0]] 
    [[1], [0]] 
    

    サンプル無効な入力:

    [[2]] # 2 is more than N=1 (total number of sublists) 
    [[0, 1], [2, 0]] # 2 is equal to N=2 (total number of sublists) 
    

    私がしようとしている言い換えれば、サブリスト内の各値は、整数> = 0と< N

サンプル有効な入力でありますプロパティベースのテストでそれに接近し、hypothesis libraryと異なる有効な入力を生成し、私の頭をのまわりで包み込みたいとintegers()、しかしそれを動作させることはできません。

  • 条件#1は、条件#2が条件#3があるChaining strategies together
  • の下で覆われているlists()min_sizemax_size引数
  • にアプローチするのは簡単です上記の例のrectangle_listsを使用した場合、「親」リストの長さの参照が内部にありませんintegers()

質問:

どのように私はサブリスト内の整数値は、サブリストの合計数よりも小さくなるように制限することができますか?


私の試みのいくつか:

from hypothesis import given 
from hypothesis.strategies import lists, integers 

@given(lists(lists(integers(min_value=0, max_value=5), min_size=1, max_size=5), min_size=1, max_size=50)) 
def test(l): 
    # ... 

この1つは非常に遠くの要件を満たしてからだった - リストは厳密に矩形状のものではなく、生成された整数値は、リストの生成サイズの上に行くことができます。ここ

from hypothesis import given 
from hypothesis.strategies import lists, integers 

@given(integers(min_value=0, max_value=5).flatmap(lambda n: lists(lists(integers(min_value=1, max_value=5), min_size=n, max_size=n), min_size=1, max_size=50))) 
def test(l): 
    # ... 

、#1及び#2は、要件満たされたが、整数値がリストのサイズよりも大きい行くことができている - 要件#3が満たされていません。

答えて

5

このようなトリッキーな制約を解決しようとするときに便利な一般的な手法があります。必要なもののように見えるものを作成してください。ただし、すべての制約を満たしていないものを作成し、 (例えば、悪いビットを投げ捨てたり、動作していないビットにパッチを当てることによって)それを修正して、それが制約を満たすようにする。この我々は

from hypothesis.strategies import builds, lists, integers 

def prune_list(ls): 
    n = len(ls) 
    return [ 
     [i for i in sublist if i < n][:5] 
     for sublist in ls 
    ] 

limited_list_strategy = builds(
    prune_list, 
    lists(lists(integers(0, 49), average_size=5), max_size=50, min_size=1) 
) 

:あなたのケースのために

は、次のような何かができ

  1. は、大きく右に見えるのリストを生成する(これは、整数とのリストのリストです整数はすべて可能なインデックスと同じ範囲にあります)。
  2. サブリスト
  3. から任意の無効なインデックスがまだ結果はあなたが必要なすべての3つの条件を満たさなければならない。それらの中に5つの以上の要素

を持っている任意のサブリストを切り捨ててプルーン。

average_sizeパラメータは厳密には必要ではありませんが、これを試してみると、それ以外の場合は空のサブリストが生成されやすくなります。

ETA:申し訳ありません。私はちょうど私があなたの条件の1つを間違って読んだことに気づいた - これは実際にあなたが望むものをしません。なぜなら、各リストが同じ長さであることを保証しないからです。ここでは、これはそれを修正する修正する方法です(それは少し複雑になりますが、私は代わりの複合材を使用するように切り替えたビルド):

from hypothesis.strategies import composite, lists, integers, permutations 


@composite 
def limisted_lists(draw): 
    ls = draw(
     lists(lists(integers(0, 49), average_size=5), max_size=50, min_size=1) 
    ) 
    filler = draw(permutations(range(50))) 
    sublist_length = draw(integers(0, 5)) 

    n = len(ls) 
    pruned = [ 
     [i for i in sublist if i < n][:sublist_length] 
     for sublist in ls 
    ] 

    for sublist in pruned: 
     for i in filler: 
      if len(sublist) == sublist_length: 
       break 
      elif i < n: 
       sublist.append(i) 
    return pruned 

アイデアは、私たちが提供する「フィラー」リストを生成することですサブリストがどのように見えるかについてのデフォルト値(したがって、それらは互いに対してより似ている方向に縮小する傾向があります)を作成し、サブリストの長さを引き出してその一貫性を得るようにします。

これはかなり複雑ですが、私は認めます。 RecursivelyIronicのフラットマップベースのバージョンを使用することもできます。私がこれを好む主な理由は、それがより良く縮小する傾向があることです。そのため、より良い例が得られます。

+1

偉大な、私は考えを得た!この素晴らしいライブラリの著者がSOを手伝ってくれてうれしいです。答えと仮説そのものに感謝します。それは傑作です! – alecxe

+1

大歓迎です。あなたが図書館を楽しんでいるとうれしいです。 :-) – DRMacIver

4

flatmapでこれを行うこともできますが、これは少しの歪みです。

from hypothesis import strategies as st 
from hypothesis import given, settings 

number_of_lists = st.integers(min_value=1, max_value=50) 
list_lengths = st.integers(min_value=0, max_value=5) 

def build_strategy(number_and_length): 
    number, length = number_and_length 
    list_elements = st.integers(min_value=0, max_value=number - 1) 
    return st.lists(
     st.lists(list_elements, min_size=length, max_size=length), 
     min_size=number, max_size=number) 

mystrategy = st.tuples(number_of_lists, list_lengths).flatmap(build_strategy) 

@settings(max_examples=5000) 
@given(mystrategy) 
def test_constraints(list_of_lists): 
    N = len(list_of_lists) 

    # condition 1 
    assert 1 <= N <= 50 

    # Condition 2 
    [length] = set(map(len, list_of_lists)) 
    assert 0 <= length <= 5 

    # Condition 3 
    assert all((0 <= element < N) for lst in list_of_lists for element in lst) 

ダビデが述べたように、これは空のリストを大量に生成する傾向があるんので、いくつかの平均サイズの調整が必要とされるであろう。

>>> mystrategy.example() 
[[24, 6, 4, 19], [26, 9, 15, 15], [1, 2, 25, 4], [12, 8, 18, 19], [12, 15, 2, 31], [3, 8, 17, 2], [5, 1, 1, 5], [7, 1, 16, 8], [9, 9, 6, 4], [22, 24, 28, 16], [18, 11, 20, 21], [16, 23, 30, 5], [13, 1, 16, 16], [24, 23, 16, 32], [13, 30, 10, 1], [7, 5, 14, 31], [31, 15, 23, 18], [3, 0, 13, 9], [32, 26, 22, 23], [4, 11, 20, 10], [6, 15, 32, 22], [32, 19, 1, 31], [20, 28, 4, 21], [18, 29, 0, 8], [6, 9, 24, 3], [20, 17, 31, 8], [6, 12, 8, 22], [32, 22, 9, 4], [16, 27, 29, 9], [21, 15, 30, 5], [19, 10, 20, 21], [31, 13, 0, 21], [16, 9, 8, 29]] 
>>> mystrategy.example() 
[[28, 18], [17, 25], [26, 27], [20, 6], [15, 10], [1, 21], [23, 15], [7, 5], [9, 3], [8, 3], [3, 4], [19, 29], [18, 11], [6, 6], [8, 19], [14, 7], [25, 3], [26, 11], [24, 20], [22, 2], [19, 12], [19, 27], [13, 20], [16, 5], [6, 2], [4, 18], [10, 2], [26, 16], [24, 24], [11, 26]] 
>>> mystrategy.example() 
[[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []] 
>>> mystrategy.example() 
[[], [], [], [], [], [], [], [], [], [], [], [], [], [], []] 
>>> mystrategy.example() 
[[6, 8, 22, 21, 22], [3, 0, 24, 5, 18], [16, 17, 25, 16, 11], [2, 12, 0, 3, 15], [0, 12, 12, 12, 14], [11, 20, 6, 6, 23], [5, 19, 2, 0, 12], [16, 0, 1, 24, 10], [2, 13, 21, 19, 15], [2, 14, 27, 6, 7], [22, 25, 18, 24, 9], [26, 21, 15, 18, 17], [7, 11, 22, 17, 21], [3, 11, 3, 20, 16], [22, 13, 18, 21, 11], [4, 27, 21, 20, 25], [4, 1, 13, 5, 13], [16, 19, 6, 6, 25], [19, 10, 14, 12, 14], [18, 13, 13, 16, 3], [12, 7, 26, 26, 12], [25, 21, 12, 23, 22], [11, 4, 24, 5, 27], [25, 10, 10, 26, 27], [8, 25, 20, 6, 23], [8, 0, 12, 26, 14], [7, 11, 6, 27, 26], [6, 24, 22, 23, 19]] 
+0

追加の明示的なコメントとアサーションに感謝します!完璧に動作します! – alecxe

1

かなり遅れていますが、後世のために:最も簡単な解決策は、ディメンションを選択して要素戦略から構築することです。

from hypothesis.strategies import composite, integers, lists 

@composite 
def complicated_rectangles(draw, max_N): 
    list_len = draw(integers(1, max_N)) 
    sublist_len = draw(integers(0, 5)) 
    element_strat = integers(0, min(list_len, 5)) 
    sublist_strat = lists(
     element_strat, min_size=sublist_len, max_size=sublist_len) 
    return draw(lists(
     sublist_strat, min_size=list_len, max_size=list_len)) 
関連する問題