2011-02-06 17 views
5

私はTable[expr,{x,...}]は私が使用して終了レシピが断続的に失敗したx非標準の評価とPackedArray

を守るのと同じ方法で、現在のコンテキストで評価からプレースホルダ記号xを保護allTrue[{x,list},test]機能を作る方法をasked以前持っていますリストをPackedArraysに自動的に変換することによって問題が発生していることが判明しました。ここで私は自動的にPackedArrayに変換されるかどうかに関係なく{1,2,3}Trueを返すようにallTrue[{x,{1,2,3}},x>0]をしたい失敗例

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] 

だ、それを実装するための良い方法は何ですか?

+0

パフォーマンスの影響についてはわかりませんが、ほとんどの場合、LengthWhile [FromPackedArray @ lis'、...作品 –

+0

はい、これは私が今使っているものですが、私はこの種のタスクの全体的なスタイルにもっと興味があります –

+2

ドキュメントから** ToPackedArrayを使用してもMathematicaで生成された結果は変わりません** ... lier!リアー!火のズボン! –

答えて

7

これは私がかなり長い間使ってきたバージョンです(本来、私の本の第2版のために書いたものですが、私はそれをたくさん使ってしまいました)。引数が評価されていないコードを表している場合、特定の1つの句を表す1つのコードが未評価の形で渡されるようにするには、テスト関数に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]]]; 

編集:私はちょうど疑問にリンクされたページの下部にあるダニエル・リーブスにより、溶液は、これと非常に似ていることに気づきました。主な違いは、短絡と引数の評価が未評価であることです(下記参照)。一方、Danielは短絡部分のみに焦点を当てています。

これは短絡動作をしています。引数を未評価の形で保存したいので、HoldRest属性が必要です。 testに渡されるまで未評価の各引数を保持するために、純関数内にHoldAll(またはHoldFirst)属性も必要です。それがtestの本文で使用される前に評価されるかどうかは、今度はtestの属性に依存します。例として:

ここでは、副作用(印刷)を引き起こすいくつかのコードを引数として渡す例を示します。 fastOrOrのための句として評価されないコードの一般的な部分を受け入れているので、あなたがする必要がある、ということ

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 

注:結果はすでに前項で決定されているので、最後の引数のコードは、実行する機会を持っていません上記のRangeの例のように、開始時に評価されても構わない場合は、Evaluateに値リストをラップしてください。

最後に、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]] 

は、我々はプログラムで印刷-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を使用します(一般的なイディオムがAppend[Hold[parts___],Unevaluated[newpart]]のように見えるように、我々は我々が追加されている作品をしたくない場合は、一般的には、それを注意/評価するために、先頭に追加し、我々は、Unevaluatedでそれをラップする必要があります):

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.0.0.0のパックド配列のバグがあり、8.0.1のソースで修正されていることです。つまり、8.0.1から、私のバージョンかMichaelバージョンを使用できます。

HTH

編集:

私はちょうどあなたが言及記事で、あなたは別の構文を望んでいたことに気づきました。このケースに対して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; 
]; 

説明のいくつかの単語:私たちは一度だけ定義する必要がカスタム例外タグ以来のトップレベルにモジュールを使用し、同様の定義時にそれを行うことができます。表から脱出する方法は例外を通じたものです。それほどエレガントではなく、小さなパフォーマンスヒットを引き起こしますが、Tableによって行われるイテレータ変数の自動動的ローカライゼーションとシンプルさを購入しています。これを安全な方法で行うには、一意のタグで例外にタグを付ける必要があります。したがって、誤って他の例外を検出することはありません。永続的な例外タグを作成するためにモジュールを使用することは、一般的に非常に便利なトリックとなることがわかりました。さて、いくつかの例は: - これは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への割り当てを開始します。

lstはリストを表しているという事実から
In[47]:= lst = Range[5]; 
AllTrue[{i, lst}, i > 3] 

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

(:最後に、(私は他の場所でコメントしたように)、AllTrueAnyTrueのためのあなたのオリジナルの署名は、以下が動作しないという意味で、少しも限定的であることに注意してくださいHoldAll属性のため、パターンマッチング時には分かりません)。この動作を維持する正当な理由はないので、_Listのチェックを削除することができます。AnyTrue[iter : {var_Symbol, lis_}, expr_]と同様にAllTrueを使用します。このクラスの使用例について説明します。

+0

これは文字通り素晴らしいです。組み込みのMathematicaの関数型プログラミング構造は、 'every 'と' some'を達成するためにこれほど多くの仕事をしなければならない場合、少し貧困に見舞われるようです。 – Harold

+0

@Haroldこれは、すでに計算された式ではなく、評価されていないコードの塊を処理するため、通常は「every」と「some」ではありません。そしてこれほど多くの仕事ではなかったが、ポストのほとんどは教育的であり、単に実装ではなく説明に専念している。また、私のコードが物事を過剰に拡大しているかもしれません。 –

+0

ああ? 「every」と「some」の「通常の」定義が増えていますか?私は、公式の文書に何も組み込まれていないのを見ました。 (しかし、私はまだそこにあるものを見つけることで改善しています)おそらく、これをMathematica.stackexchange.comへの質問として追加する必要があります。 – Harold

関連する問題