Нестандартная оценка и PackedArray - PullRequest
5 голосов
/ 06 февраля 2011

Я ранее спрашивал , как сделать allTrue[{x,list},test] функцию, которая защищает символ-заполнитель x от оценки в текущем контексте так же, как Table[expr,{x,...}] защищает x

Рецепт, который я использовал в конце концов, периодически прерывался, и я обнаружил, что проблема вызвана автоматическим преобразованием списков в PackedArrays. Вот неудачный пример

SetAttributes[allTrue, HoldAll];
allTrue[{var_, lis_}, expr_] := 
  LengthWhile[lis, 
    TrueQ[ReleaseHold[Hold[expr] /. HoldPattern[var] -> #]] &] == 
   Length[lis];
allTrue[{y, Developer`ToPackedArray[{1, 1, 1}]}, y > 0]

Я хочу, чтобы allTrue[{x,{1,2,3}},x>0] вернул True независимо от того, автоматически ли {1,2,3} преобразуется в PackedArray, как лучше его реализовать?

1 Ответ

7 голосов
/ 06 февраля 2011

Это версия, которой я пользуюсь довольно давно (изначально она была написана для второго издания моей книги, но я часто пользовался ею). Если аргументы представляют некоторый неоцененный код, тогда у тестовой функции должны быть атрибуты HoldAll или HoldFirst, если мы хотим, чтобы один фрагмент кода, представляющий одно конкретное предложение, был передан ему в его неоцененной форме (что может или не может быть желательным). ).

ClearAll[fastOr];
Attributes[fastOr] = {HoldRest};
fastOr[test_, {args___}] := fastOr[test, args];
fastOr[test_, args___] :=
TrueQ[Scan[
        Function[arg, If[test[arg], Return[True]], HoldAll],
        Hold[args]]];

Редактировать: Я только что заметил, что решение Дэниэла Ривза внизу страницы, на которую ссылается вопрос, очень похоже на это. Основное отличие состоит в том, что я забочусь о закорачивании и сохранении аргументов без оценки (см. Ниже), в то время как Даниэль фокусируется только на закорачивающей части.

Он имеет короткое замыкание. Нам нужен атрибут HoldRest, поскольку мы хотим сохранить аргументы в их неоцененной форме. Нам также нужен атрибут HoldAll (или HoldFirst) в чистой функции, чтобы сохранить каждый из аргументов без оценки до тех пор, пока он не будет передан test. Будет ли он оценен до того, как его использовать в теле test, теперь зависит от атрибутов test. Как пример:

Clear[fullSquareQ];
fullSquareQ[x_Integer] := IntegerQ[Sqrt[x]];

In[13]:= Or @@ Map[fullSquareQ, Range[50000]] // Timing

Out[13]= {0.594, True}

In[14]:= fastOr[fullSquareQ, Evaluate[Range[10000]]] // Timing

Out[14]= {0., True}

Вот пример, где мы передаем в качестве аргументов некоторые фрагменты кода, вызывающие побочные эффекты (печать). Код последнего аргумента не имеет возможности выполнить, так как результат уже был определен в предыдущем предложении:

In[15]:= fastOr[# &, Print["*"]; False, Print["**"]; False, 
  Print["***"]; True, Print["****"]; False]

During evaluation of In[15]:= *

During evaluation of In[15]:= **

During evaluation of In[15]:= ***

Out[15]= True

Обратите внимание, что, поскольку fastOr принимает общие фрагменты неоцененного кода в качестве предложений для Or, вы должны обернуть свой список значений в Evaluate, если вам все равно, что они будут оцениваться на начало (как в случае с примером Range выше).

Наконец, я проиллюстрирую программную конструкцию удерживаемого кода для fastOr, чтобы показать, как его можно использовать (если хотите, рассмотрите крошечный ускоренный курс по работе с удерживаемыми выражениями). Следующая функция очень полезна при работе с удерживаемыми выражениями:

joinHeld[a___Hold] := Hold @@ Replace[Hold[a], Hold[x___] :> Sequence[x], {1}];

Пример:

In[26]:= joinHeld[Hold[Print[1]], Hold[Print[2], Print[3]], Hold[], Hold[Print[4]]]

Out[26]= Hold[Print[1], Print[2], Print[3], Print[4]]

Вот как мы используем его для программного конструирования удерживаемых аргументов, которые использовались в примере с Print-s выше:

In[27]:= 
held = joinHeld @@ MapThread[Hold[Print[#]; #2] &, 
      {NestList[# <> "*" &, "*", 3], {False, False, True, False}}]

Out[27]= Hold[Print["*"]; False, Print["**"]; False, Print["***"]; True, Print["****"]; False]

Чтобы передать его в fastOr, мы будем использовать другую полезную идиому: добавляем (или добавляем) к Hold[args], пока не получим все аргументы функции, а затем используем Apply (обратите внимание, что, как правило, если мы не Если мы не хотим оценивать кусок, который мы добавляем / готовим, мы должны обернуть его в Unevaluated, поэтому общая идиома выглядит как Append[Hold[parts___],Unevaluated[newpart]]):

In[28]:= fastOr @@ Prepend[held, # &]

During evaluation of In[28]:= *

During evaluation of In[28]:= **

During evaluation of In[28]:= ***

Out[28]= True

Относительно оригинальной реализации, на которую вы ссылаетесь, вы можете посмотреть мой комментарий к ней, который я сделал некоторое время назад. Проблема в том, что в TakeWhile и LengthWhile есть ошибки для упакованных массивов в v. 8.0.0, они исправлены в источниках 8.0.1 - так что, начиная с 8.0.1, вы можете использовать либо мою, либо версию Майкла.

НТН

Edit:

Я только что заметил, что в посте, на который вы ссылались, вы хотели использовать другой синтаксис. Хотя было бы не очень трудно принять подход, принятый в fastOr, к этому случаю, здесь есть другая реализация, которая, возможно, находится в более тесном соответствии с существующими языковыми конструкциями для этого конкретного синтаксиса. Я предлагаю использовать Table и исключения, поскольку итераторы в Table принимают тот же синтаксис, что и вы. Вот оно:

ClearAll[AnyTrue, AllTrue];
SetAttributes[{AnyTrue, AllTrue}, HoldAll];
Module[{exany, exall},
 AnyTrue[iter : {var_Symbol, lis_List}, expr_] :=
    TrueQ[Catch[Table[If[TrueQ[expr], Throw[True, exany]], iter], exany]];
 AllTrue[iter : {var_Symbol, lis_List}, expr_] :=
   Catch[Table[If[! TrueQ[expr], Throw[False, exall]], iter], exall] =!= False;
];

Несколько слов объяснения: я использую Module на верхнем уровне, поскольку пользовательские теги исключений, которые мы должны определить только один раз, могут также делать это во время определения. Способ вырваться из таблицы через исключения. Не очень элегантно и приводит к небольшому падению производительности, но мы покупаем автоматическую динамическую локализацию переменных вашего итератора с помощью Table и простоту. Чтобы сделать это безопасным способом, мы должны пометить исключение уникальным тегом, чтобы не перехватить другое исключение по ошибке. Я считаю, что использование модуля для создания постоянных тегов исключений является очень полезным приемом в целом. Теперь несколько примеров:

In[40]:= i = 1

Out[40]= 1

In[41]:= AnyTrue[{i, {1, 2, 3, 4, 5}}, i > 3]

Out[41]= True

In[42]:= AnyTrue[{i, {1, 2, 3, 4, 5}}, i > 6]

Out[42]= False

In[43]:= AllTrue[{i, {1, 2, 3, 4, 5}}, i > 3]

Out[43]= False

In[44]:= AllTrue[{i, {1, 2, 3, 4, 5}}, i < 6]

Out[44]= True

In[45]:= AllTrue[{a, {1, 3, 5}}, AnyTrue[{b, {2, 4, 5}}, EvenQ[a + b]]]

Out[45]= True

In[46]:= AnyTrue[{a, {1, 3, 5}}, AllTrue[{b, {2, 4, 5}}, EvenQ[a + b]]]

Out[46]= False

Я начал с присваивания i, чтобы показать, что возможные глобальные значения переменных итератора не имеют значения - об этом позаботится Table. Наконец, обратите внимание, что (как я комментировал в другом месте) ваши оригинальные подписи для AllTrue и AnyTrue слишком ограничительны, в том смысле, что следующее не работает:

In[47]:= lst = Range[5];
AllTrue[{i, lst}, i > 3]

Out[48]= AllTrue[{i, lst}, i > 3] 

(поскольку тот факт, что lst представляет список, неизвестен во время сопоставления с образцом из-за атрибута HoldAll). Нет веских причин для сохранения этого поведения, поэтому вы можете просто удалить проверки _List: AnyTrue[iter : {var_Symbol, lis_}, expr_] и аналогично для AllTrue, и этот класс вариантов использования будет рассмотрен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...