Функция f
ссылается на list
как свободную переменную, а не как параметр.Хотя это не является непреодолимым препятствием, оно затрудняет упаковку этой функциональности, чтобы ее можно было использовать в Table
.Давайте доработаем эти определения и применим некоторые упрощения.
Во-первых, давайте приступим к проверке того, является ли образец приемлемым:
acceptableQ[sample_] :=
MemberQ[sample, n_ /; n > 31] &&
1 <= Count[sample, n_ /; n <= 12] <= 2 &&
Count[sample, n_ /; divisible2to7[n]] <= 3
divisible2to7[n_] := MemberQ[Range[2, 7], d_ /; Divisible[n, d]]
Основное упрощение из оригинала заключается в том, чтовложенные операторы If
сведены в состояние And
.Новое определение также использует тот факт, что Count
может тестировать значения списка без необходимости вызывать вложенный Select
.Кроме того, проверки существования были выражены с использованием MemberQ[...]
.Была введена вспомогательная функция для проверки делимости, чтобы уменьшить визуальную сложность основного выражения теста.Обратите внимание, что исходная проверка делимости неправильно возвращала список, в котором ожидалось логическое значение.Тесты _Integer
head были удалены, но если они считаются желательными, их можно ввести заново, изменив каждый n_
на n_Integer
.
Теперь нам просто нужен способ генерирования выборок в циклепока не будет найден приемлемый:
generateSample[] :=
While[
True
, RandomSample[Range[36], 7] /.
s_ :> If[acceptableQ[s], Return[Sort @ s]]
]
generateSample[]
теперь можно использовать для создания таблицы с таким количеством результатов, сколько необходимо:
In[113]:= Table[generateSample[], {5}]
Out[113]= {{6, 13, 17, 19, 25, 29, 33}, {1, 11, 13, 15, 31, 35, 36},
{1, 10, 17, 23, 25, 31, 32}, {1, 6, 17, 19, 22, 23, 33},
{8, 17, 19, 23, 30, 31, 36}}
Обобщение шаблона
Параметр, воплощенный в generateSample
, может быть параметризован для принятия произвольных функций генератора и фильтра:
SetAttributes[generatorSelect, HoldFirst]
generatorSelect[generator_, predicate_] :=
While[True, generator /. s_ :> If[predicate[s], Return[s]]]
Аргумент generator
сохраняется в неоцененной форме, чтобы его можно было оценитьзаново на каждом проходе через цикл.Эту новую функцию можно использовать следующим образом:
In[114]:= Table[
generatorSelect[RandomSample[Range[36], 7], acceptableQ] // Sort
, {5}
]
Out[114]= {{9, 17, 19, 23, 27, 29, 32}, {8, 13, 17, 19, 22, 23, 35},
{4, 17, 19, 21, 23, 29, 36}, {1, 8, 15, 19, 23, 31, 33},
{1, 10, 17, 19, 24, 29, 36}}
Преимущество новой функции заключается в том, что ее можно использовать с любыми функциями генератора и фильтра.Здесь мы генерируем кортежи из трех целых чисел, которые составляют до семи.
In[115]:= Table[
generatorSelect[RandomInteger[7, 3], Total[#] == 7 &]
, {5}
]
Out[115]= {{2, 3, 2}, {0, 5, 2}, {5, 0, 2}, {2, 4, 1}, {2, 1, 4}}
В качестве стиля некоторые предпочитают избегать определения функций с атрибутами Hold
, если в этом нет крайней необходимости.generatorSelect2
отражает этот выбор дизайна:
generatorSelect2[generator_, predicate_] :=
While[True, generator[] /. s_ :> If[predicate[s], Return[s]]]
Единственное различие между этим и generatorSelect
состоит в том, что теперь ожидается, что первый аргумент будет вычисляться для функции:
In[116]:= Table[
generatorSelect2[RandomInteger[7, 3] &, Total[#] == 7 &]
, {5}
]
Out[116]= {{5, 1, 1}, {3, 0, 4}, {0, 1, 6}, {3, 2, 2}, {4, 1, 2}}