سؤال

أريد شيئا من هذا القبيل:

each[i_, {1,2,3},
  Print[i]
]

أو، بشكل أكثر عمومية، لتدمير العناصر العشوائية في القائمة التي تقوم بالتكرار عليها، مثل:

each[{i_, j_}, {{1,10}, {2,20}, {3,30}},
  Print[i*j]
]

عادة ما تريد استخدامه Map أو غيرها من التركيبات الوظيفية البحتة وتجنب أسلوب البرمجة غير الوظيفي حيث تستخدم التأثيرات الجانبية.ولكن فيما يلي مثال حيث أعتقد أن البنية لكل منها مفيدة للغاية:

لنفترض أن لدي قائمة من الخيارات (القواعد) التي تربط الرموز بالتعبيرات، مثل

attrVals = {a -> 7, b -> 8, c -> 9}

الآن أريد إنشاء جدول تجزئة حيث أقوم بالتخطيط الواضح لتلك الرموز لتلك الأرقام.لا أعتقد أن هناك طريقة أنظف للقيام بذلك من

each[a_ -> v_, attrVals, h[a] = v]

حالات اختبار إضافية

في هذا المثال، نقوم بتحويل قائمة المتغيرات:

a = 1;
b = 2;
c = 3;
each[i_, {a,b,c}, i = f[i]]

بعد ما سبق، {a,b,c} ينبغي تقييم ل {f[1],f[2],f[3]}.لاحظ أن هذا يعني الوسيطة الثانية ل each ينبغي أن تبقى دون تقييم إذا كانت قائمة.

إذا لم يكن النموذج الذي لم يتم تقييمه قائمة، فيجب تقييم الوسيطة الثانية.على سبيل المثال:

each[i_, Rest[{a,b,c}], Print[i]]

يجب أن طباعة قيم b و c.

إضافة:للقيام بكل صحيح، ينبغي أن تدعم Break[] و Continue[].لست متأكدا من كيفية تنفيذ ذلك.ربما ستحتاج إلى تنفيذها بطريقة أو بأخرى من حيث For أو while أو Do نظرًا لأن هذه هي بنيات الحلقة الوحيدة التي تدعمها Break[] و Continue[].

ومشكلة أخرى في الإجابات حتى الآن:هم ياكلون Return[]س.بمعنى، إذا كنت تستخدم حلقة ForEach في دالة وتريد العودة من الدالة من داخل الحلقة، فلن تتمكن من ذلك.يبدو أن إصدار Return داخل حلقة ForEach يعمل بنفس الطريقة Continue[].هذا فقط (انتظر) ألقى بي في حلقة.

هل كانت مفيدة؟

المحلول 7

Pillsy و <لأ href = "https://stackoverflow.com/users/565518/leonid- shifrin "> ليونيد Shifrin ، وهنا ما أنا الآن باستخدام:

SetAttributes[each, HoldAll];               (* each[pattern, list, body]      *)
each[pat_, lst_List, bod_] :=               (*  converts pattern to body for  *)
  (Cases[Unevaluated@lst, pat:>bod]; Null); (*   each element of list.        *)
each[p_, l_, b_] := (Cases[l, p:>b]; Null); (* (Break/Continue not supported) *)

نصائح أخرى

تحتوي الإصدارات الأحدث من Mathematica (6.0+) على إصدارات معممة من Do[] وTable[] التي تفعل ما تريده بدقة تقريبًا، من خلال اتخاذ شكل بديل من وسيطة التكرار.على سبيل المثال،

Do[
  Print[i],
  {i, {1, 2, 3}}]

هو تماما مثل الخاص بك

ForEach[i_, {1, 2, 3,},
  Print[i]]

بدلاً من ذلك، إذا كنت تحب صيغة ForEach المحددة، فيمكنك إنشاء وظيفة HoldAll التي تنفذها، كما يلي:

Attributes[ForEach] = {HoldAll};

ForEach[var_Symbol, list_, expr_] :=
  ReleaseHold[
    Hold[
      Scan[
        Block[{var = #},
         expr] &,
      list]]];

ForEach[vars : {__Symbol}, list_, expr_] :=
  ReleaseHold[
    Hold[
      Scan[
        Block[vars,
          vars = #;
          expr] &,
      list]]];

يستخدم هذا الرموز كأسماء متغيرة، وليس أنماطًا، ولكن هذه هي الطريقة التي تعمل بها هياكل التحكم المضمنة المتنوعة مثل Do[] وFor[].

تسمح لك وظائف HoldAll[] بتجميع مجموعة كبيرة جدًا من هياكل التحكم المخصصة.عادةً ما تكون ReleaseHold[Hold[...]] هي الطريقة الأسهل لتجميع مجموعة من أكواد Mathematica البرمجية ليتم تقييمها لاحقًا، كما يسمح Block[{x = #}, ...]& بربط المتغيرات في نص التعبير الخاص بك بـ مهما كانت القيم التي تريدها.

ردًا على سؤال dreeves أدناه، يمكنك تعديل هذا الأسلوب للسماح بمزيد من التدمير التعسفي باستخدام القيم المنخفضة لرمز فريد.

ForEach[patt_, list_, expr_] := 
  ReleaseHold[Hold[
     Module[{f}, 
       f[patt] := expr; 
       Scan[f, list]]]]

ومع ذلك، في هذه المرحلة، أعتقد أنه قد يكون من الأفضل لك بناء شيء ما فوق الحالات.

ForEach[patt_, list_, expr_] :=
  With[{bound = list},
    ReleaseHold[Hold[
       Cases[bound,
         patt :> expr]; 
       Null]]]

أحب جعل Null واضحًا عندما أقوم بقمع القيمة المرجعة للدالة. يحرر:لقد أصلحت الخلل المشار إليه أدناه؛أنا دائما أحب استخدام With لاستكمال التعبيرات التي تم تقييمها في Hold* نماذج.

وأنا سنوات في وقت متأخر إلى الحزب هنا، وهذا هو ربما أكثر جوابا على "سؤالا ميتا"، ولكن شيء كثير من الناس لديهم في البداية وقتا عصيبا مع عندما البرمجة في الرياضيات (أو لغات الفنية الأخرى) يقترب مشكلة من وظيفي وليس وجهة نظر الهيكلي. اللغة والرياضيات لديها ثوابت الهيكلية، ولكن انها تعمل في صميمها.

والنظر في المثال الأول:

ForEach[i_, {1,2,3},
  Print[i]
]

وكما أشار العديد من الناس، وهذا يمكن التعبير عنها وظيفيا كما Scan[Print, {1,2,3}] أو Print /@ {1,2,3} (على الرغم من أن صالح Scan على Map عندما يكون ذلك ممكنا، كما سبق بيانه، ولكن يمكن أن تكون مزعجة في بعض الأحيان، حيث لا يوجد مشغل أقحم لScan).

في الرياضيات، وهناك عادة عشرات الطرق لفعل كل شيء، والتي هي جميلة في بعض الأحيان ومحبطة في بعض الأحيان. مع أخذ ذلك في الاعتبار، والنظر في المثال الثاني:

ForEach[{i_, j_}, {{1,10}, {2,20}, {3,30}},
  Print[i*j]
]

... الذي هو أكثر إثارة للاهتمام من جهة نظر وظيفية.

وحل واحد وظيفية الممكن هو بدلا من استخدام استبدال قائمة، منها مثلا:

In[1]:= {{1,10},{2,20},{3,30}}/.{i_,j_}:>i*j
Out[1]= {10,40,90}

و... ولكن إذا كانت قائمة كبيرة جدا، وهذا من شأنه أن يكون بطيئا دون داع منذ نقوم به ما يسمى ب "مطابقة النمط" (على سبيل المثال، وتبحث عن حالات {أ، ب} في القائمة وتكليفهم i وj) بلا سبب.

وبالنظر إلى مجموعة كبيرة من 100000 أزواج، array = RandomInteger[{1, 100}, {10^6, 2}]، يمكننا أن ننظر في بعض توقيت:

والقاعدة استبدال سريع جدا:

In[3]:= First[Timing[array /. {i_, j_} :> i*j;]]
Out[3]= 1.13844

... ولكن يمكننا أن نفعل ما هو أفضل قليلا إذا ما أخذنا في الاستفادة من بنية التعبير حيث كل زوج هو حقا List[i,j] وتطبيق Times على رأس كل زوج، وتحول كل {i,j} إلى Times[i,j]:

In[4]:= (* f@@@list is the infix operator form of Apply[f, list, 1] *)
    First[Timing[Times @@@ array;]]
Out[4]= 0.861267

وكما هو مستخدم في تنفيذ ForEach[...] أعلاه، Cases هو دون المستوى الأمثل بالتأكيد:

In[5]:= First[Timing[Cases[array, {i_, j_} :> i*j];]]
Out[5]= 2.40212

... منذ Cases يفعل المزيد من العمل من مجرد استبدال القاعدة، الحاجة إلى بناء إخراج عناصر مطابقة واحدة تلو الأخرى. اتضح يمكننا القيام بالكثير <م> أفضل من خلال تحلل المشكلة بشكل مختلف، والاستفادة من حقيقة أن Times هو Listable، وتدعم عملية vectorized.

والسمة Listable يعني أن f وظيفة والخيط تلقائيا على أي الحجج القائمة:

In[16]:= SetAttributes[f,Listable]
In[17]:= f[{1,2,3},{4,5,6}]
Out[17]= {f[1,4],f[2,5],f[3,6]}

وهكذا، منذ Times هو Listable، إذا بدلا من ذلك كان لدينا أزواج من الأرقام عن اثنين من صفائف منفصلة:

In[6]:= a1 = RandomInteger[{1, 100}, 10^6];
        a2 = RandomInteger[{1, 100}, 10^6];

In[7]:= First[Timing[a1*a2;]]
Out[7]= 0.012661

نجاح باهر ، لا بأس به أسرع! حتى إذا لم يتم توفير المدخلات واثنين من صفائف منفصلة (أو لديك عناصر أكثر من اثنين في كل زوج،) لا يزال بوسعنا أن نفعل شيئا الأمثل:

In[8]:= First[Timing[Times@@Transpose[array];]]
Out[8]= 0.020391

وإن المغزى من هذه الملحمة ليست ForEach ليس بناء القيم بشكل عام، أو حتى في الرياضيات، ولكن يمكنك في كثير من الأحيان الحصول على نفس النتائج بشكل أكثر كفاءة وأكثر أناقة عند العمل في عقلية وظيفية، بدلا من واحدة الهيكلي.

ووScan المدمج في الأساس لا هذا، على الرغم من انها أقبح:

    Scan[Print[#]&, {1,2,3}]

وانها قبيحة خصوصا عندما تريد destructure العناصر:

    Scan[Print[#[[1]] * #[[2]]]&, {{1,10}, {2,20}, {3,30}}]

والدالة التالية يتجنب القبح من خلال تحويل pattern إلى body لكل عنصر من عناصر list.

SetAttributes[ForEach, HoldAll];
ForEach[pat_, lst_, bod_] :=  Scan[Replace[#, pat:>bod]&, Evaluate@lst]

والتي يمكن استخدامها كما في المثال في هذه المسألة.

وPS: الجواب المقبولة التي يسببها لي أن التحول إلى هذا، وهذا ما أنا أستعمل أي وقت مضى منذ ويبدو أن العمل العظيم (باستثناء التحذير I إلحاق السؤال):

SetAttributes[ForEach, HoldAll];             (* ForEach[pattern, list, body]   *)
ForEach[pat_, lst_, bod_] := ReleaseHold[    (*  converts pattern to body for  *)
  Hold[Cases[Evaluate@lst, pat:>bod];]];     (*   each element of list.        *)

وظيفة خريطة المدمج في يفعل بالضبط ما تريد. ويمكن استخدامه في شكل طويل:

خريطة [طباعة، {1،2،3}]

وأو مختزلة

وطباعة / @ {1،2،3}

في الحالة الثانية الخاصة بك، وكنت تستخدم "طباعة [تايمز#] و/ @ {{1،10}، {2،20}، {3،30}}"

وأنا أنصح بقراءة التعليمات الرياضيات على الخريطة، MapThread، تطبيق، وظيفة. ويمكن أن تأخذ قليلا من التعود، ولكن بمجرد أن تكون، فلن أريد أن أعود!

وهنا هو تحسن طفيف على أساس الإجابة الأخيرة من dreeves التي تسمح لتحديد نمط دون فارغة (صنع بناء الجملة مشابهة إلى وظائف أخرى مثل طاولة أو دو) ويستخدم حجة مستوى حالات

SetAttributes[ForEach,HoldAll];
ForEach[patt_/; FreeQ[patt, Pattern],list_,expr_,level_:1] :=
   Module[{pattWithBlanks,pattern},
      pattWithBlanks = patt/.(x_Symbol/;!MemberQ[{"System`"},Context[x]] :> pattern[x,Blank[]]);
      pattWithBlanks = pattWithBlanks/.pattern->Pattern;

      Cases[Unevaluated@list, pattWithBlanks :> expr, {level}];
      Null
   ];

والاختبارات:

ForEach[{i, j}, {{1, 10}, {2, 20}, {3, 30}}, Print[i*j]]
ForEach[i, {{1, 10}, {2, 20}, {3, 30}}, Print[i], 2]

والرياضيات لها وظائف خريطة، لذلك دعونا نقول لديك وظيفة Functaking وسيطة واحدة. ثم اكتبوا

Func /@ list

Print /@ {1, 2, 3, 4, 5}

وقيمة الإرجاع هي قائمة وظيفة تطبيقه على كل عنصر في القائمة في.

PrimeQ /@ {10, 2, 123, 555}

وسيعود {False,True,False,False}

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top