Julia: تسلسل الوظيفة

تم إنشاؤها على ٢٧ يناير ٢٠١٤  ·  232تعليقات  ·  مصدر: JuliaLang/julia

هل من الممكن السماح باستدعاء أي دالة على Any بحيث يتم تمرير القيمة إلى الوظيفة كمعامل أول ويتم إضافة المعلمات التي تم تمريرها إلى استدعاء الوظيفة على القيمة بعد ذلك؟
على سبيل المثال

sum(a::Int, b::Int) -> a + b

a = 1
sum(1, 2) # = 3
a.sum(2) # = 3 or
1.sum(2) # = 3

هل من الممكن الإشارة بطريقة حتمية إلى ما ستعيده الوظيفة لتجنب استثناءات وقت التشغيل؟

التعليق الأكثر فائدة

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

الحزم

النماذج غير الحزمة

ذات صلة:


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

تم التحديث: 2020-04-20

ال 232 كومينتر

بناء الجملة . مفيد جدًا ، لذلك لن نجعله مجرد مرادف لاستدعاء الوظيفة. لا أفهم ميزة 1.sum(2) على sum(1,2) . بالنسبة لي يبدو أن الخلط بين الأشياء.

هل السؤال عن الاستثناءات قضية منفصلة؟ أعتقد أن الإجابة هي لا ، بصرف النظر عن التفاف هيئة وظيفية في محاولة .. الصيد.

مثال 1.sum (2) تافه (أنا أيضًا أفضل sum (1،2)) ولكنه فقط لإثبات أن الوظيفة لا يمتلكها هذا النوع ex. يمكن تمرير 1 إلى دالة مع كون المعلمة الأولى هي Real ، وليس فقط إلى الوظائف التي تتوقع أن يكون المعامل الأول Int.

تحرير: ربما أكون قد أسأت فهم تعليقك. ستكون وظائف النقاط مفيدة عند تطبيق أنماط تصميم معينة مثل نمط الباني المستخدم بشكل شائع للتكوين. على سبيل المثال

validate_for(name).required().gt(3) 
# vs 
gt(required(validate_for(name)), 3) 

الاستثناءات التي أشرت إليها للتو هي بسبب الوظائف التي تعيد النتائج غير الحتمية (وهي ممارسة سيئة على أي حال). مثال على ذلك هو استدعاء a.sum (2) .sum (4) حيث يعرض .sum (2) أحيانًا سلسلة بدلاً من Int ، ولكن يتوقع .sum (4) وجود Int. أعتبر أن المترجم / وقت التشغيل ذكي بالفعل بما يكفي لتقييم مثل هذه الظروف - والتي ستكون هي نفسها عند تداخل مجموع الدالة (المجموع (1 ، 2) ، 4) - لكن طلب الميزة يتطلب توسيع الوظيفة المذكورة لفرض قيود النوع على وظائف نقطة.

واحدة من حالات الاستخدام التي يبدو أن الناس يحبونها هي "الواجهة الطلاقة". من الجيد أحيانًا في واجهات برمجة تطبيقات OOP عندما تعيد العمليات الكائن ، لذا يمكنك القيام بأشياء مثل some_obj.move(4, 5).scale(10).display()

بالنسبة لي ، أعتقد أنه من الأفضل التعبير عن هذا كتكوين دالة ، لكن |> لا يعمل مع الوسيطات ما لم تستخدم anon. وظائف ، مثل some_obj |> x -> move(x, 4, 5) |> x -> scale(x, 10) |> display ، وهو أمر قبيح جدًا.

سيكون أحد الخيارات لدعم هذا النوع من الأشياء هو إذا دفع |> LHS كأول وسيطة إلى RHS قبل التقييم ، ولكن بعد ذلك لا يمكن تنفيذه كدالة بسيطة كما هي الآن.

قد يكون الخيار الآخر نوعًا من الماكرو @composed الذي سيضيف هذا النوع من السلوك إلى التعبير التالي

يمكنك أيضًا تحويل مسؤولية دعم ذلك إلى مصممي المكتبات ، حيث يمكنهم تحديد ذلك

function move(obj, x, y)
    # move the object
end

move(x, y) = obj -> move(obj, x, y)

لذلك عندما لا تقدم كائنًا ، فإنه يقوم بتطبيق دالة جزئية (عن طريق إرجاع دالة من وسيطة واحدة) والتي يمكنك استخدامها بعد ذلك داخل سلسلة |> .

في الواقع ، من المحتمل أن يتم تغيير تعريف |> الآن إلى
السلوك الذي يطلبه. سأكون لذلك.

يوم الاثنين ، 27 يناير 2014 ، Spencer Russell [email protected]
كتب:

واحدة من حالات الاستخدام التي يبدو أن الناس يحبونها هي "الواجهة الطلاقة". انها
لطيفة في بعض الأحيان في واجهات برمجة تطبيقات OOP عندما تعيد الأساليب الكائن ، لذلك يمكنك القيام بذلك
أشياء مثل some_obj.move (4، 5). مقياس (10). العرض ()

بالنسبة لي ، أعتقد أنه من الأفضل التعبير عن هذا كتكوين وظيفة ، ولكن
لا يعمل |> مع الوسائط إلا إذا كنت تستخدم مجهول. وظائف ، على سبيل المثال some_obj
|> س -> تحرك (س ، 4 ، 5) |> س -> مقياس (س ، 10) |> عرض ، وهو جميل
البشع.

خيار واحد لدعم هذا النوع من الأشياء سيكون إذا |> دفع LHS كـ
الحجة الأولى لـ RHS قبل التقييم ، لكنها لا يمكن أن تكون كذلك
تم تنفيذها كوظيفة بسيطة كما هي الآن.

قد يكون الخيار الآخر نوعًا من @ ماكرو
نوع من السلوك للتعبير التالي

يمكنك أيضًا تحويل مسؤولية دعم هذا إلى المكتبة
المصممين ، حيث يمكنهم تحديد

تحرك الوظيفة (obj، x، y)
# حرك الكائن
النهاية

تحرك (س ، ص) = هدف -> تحرك (هدف ، س ، ص)

لذلك عندما لا تقدم كائنًا ، فإنه يتم تطبيق وظيفة جزئية
(من خلال إرجاع دالة من وسيطة واحدة) والتي يمكنك بعد ذلك استخدامها داخل ملف
عادي |> سلسلة.

-
قم بالرد على هذه الرسالة الإلكترونية مباشرة أو tHubhttps: //github.com/JuliaLang/julia/issues/5571#issuecomment -33408448
.

ssfrr تعجبني طريقة تفكيرك! لم أكن على دراية بتكوين الوظيفة |> . أرى أن هناك مناقشة مماثلة مؤخرًا [https://github.com/JuliaLang/julia/issues/4963].

kmsquire أحب فكرة تمديد تكوين الوظيفة الحالي للسماح لك بتحديد معلمات على دالة الاستدعاء ex. some_obj |> move(4, 5) |> scale(10) |> display . قد يعني الدعم الأصلي إغلاقًا واحدًا أقل ، ولكن ما اقترحه ssfrr هو طريقة قابلة للتطبيق في الوقت الحالي وكميزة إضافية ، يجب أيضًا أن يكون متوافقًا مع وظيفة تكوين الوظيفة الموسعة إذا تم تنفيذها.

شكرا على الردود السريعة :)

في الواقع ، ssfrr كان صحيحًا - ليس من الممكن تنفيذ ذلك كدالة بسيطة.

ما تريده هو سلاسل وحدات الماكرو (مثل http://clojuredocs.org/clojure_core/clojure.core/-٪3E). من المؤسف أن @ -> @ - >> @ -؟ >> غير قابل للتطبيق في بناء الجملة في جوليا.

نعم ، كنت أفكر في أن وحدات ماكرو infix ستكون طريقة لتنفيذ ذلك. لست على دراية كافية بوحدات الماكرو لمعرفة ما هي القيود.

أعتقد أن هذا يعمل مع ماكرو الإنشاء ssfrr :

تحرير: قد يكون هذا أكثر وضوحًا:

import Base.Meta.isexpr
_ispossiblefn(x) = isa(x, Symbol) || isexpr(x, :call)

function _compose(x)
    if !isa(x, Expr)
        x
    elseif isexpr(x, :call) &&    #
        x.args[1] == :(|>) &&     # check for `expr |> fn`
        length(x.args) == 3 &&    # ==> (|>)(expr, fn)
        _ispossiblefn(x.args[3])  #

        f = _compose(x.args[3])
        arg = _compose(x.args[2])
        if isa(f, Symbol)
            Expr(:call, f, arg) 
        else
            insert!(f.args, 2, arg)
            f
        end
    else
        Expr(x.head, [_compose(y) for y in x.args]...)
    end
end

macro compose(x)
    _compose(x)
end
julia> macroexpand(:(<strong i="11">@compose</strong> x |> f |> g(1) |> h('a',"B",d |> c(fred |> names))))
:(h(g(f(x),1),'a',"B",c(d,names(fred))))

إذا كان لدينا بناء الجملة |> ، فسأكون بالتأكيد أفضل لجعله أكثر فائدة مما هو عليه الآن. يبدو أن استخدام فقط للسماح بوضع الوظيفة للتطبيق على اليمين بدلاً من اليسار يبدو دائمًا وكأنه إهدار هائل لبناء الجملة.

+1. من المهم بشكل خاص عند استخدام Julia لتحليل البيانات ، حيث عادة ما يكون لديك خطوط أنابيب لتحويل البيانات. على وجه الخصوص ، يعد Pandas في Python مناسبًا للاستخدام لأنه يمكنك كتابة أشياء مثل df.groupby ("شيء"). aggregate (sum) .std (). reset_index () ، وهو كابوس للكتابة باستخدام بناء الجملة الحالي |> .

: +1: لهذا.

(لقد فكرت بالفعل في اقتراح استخدام عامل التشغيل infix .. لهذا ( obj..move(4,5)..scale(10)..display ) ، لكن عامل التشغيل |> سيكون لطيفًا أيضًا)

الاحتمال الآخر هو إضافة سكر نحوي للكاري ، مثل
f(a,~,b) الترجمة إلى x->f(a,x,b) . ثم يمكن أن يحتفظ |> بمعناه الحالي.

ووه ، ستكون هذه طريقة رائعة حقًا لتحويل أي تعبير إلى دالة.

ربما شيء مثل القيم الحرفية لوظيفة Clojure المجهولة ، حيث #(% + 5) اختصار لـ x -> x + 5 . يتم تعميم هذا أيضًا على الوسائط المتعددة باستخدام٪ 1 و٪ 2 وما إلى ذلك ، لذا فإن #(myfunc(2, %1, 5, %2) اختصار لـ x, y -> myfunc(2, x, 5, y)

من الناحية الجمالية ، لا أعتقد أن بناء الجملة يتناسب جيدًا مع جوليا المقروءة جدًا ، لكني أحب الفكرة العامة.

لاستخدام المثال أعلاه (والتبديل إلى tildemalmaud بدلاً من٪) ، يمكنك القيام بذلك

some_obj |> move(~, 4, 5) |> scale(~, 10) |> display

التي تبدو جميلة.

هذا جميل لأنه لا يعطي الحجة الأولى أي معاملة خاصة. الجانب السلبي هو أن استخدامنا بهذه الطريقة نأخذ رمزًا

ربما يكون هذا مكانًا آخر حيث يمكنك استخدام ماكرو ، لذا فإن الاستبدال يحدث فقط في سياق الماكرو.

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

map(f(_,a), v)

أي واحد يعني هذا؟

map(f(x->x,a), v)
map(x->f(x,a), v)
x->map(f(x,a), v)

كلها تفسيرات صحيحة. يبدو أنني أتذكر أن سكالا تستخدم نوع التوقيعات للوظائف لتحديد ذلك ، الأمر الذي يبدو لي أمرًا مؤسفًا لأنه يعني أنه لا يمكنك حقًا تحليل Scala دون معرفة أنواع كل شيء. لا نريد فعل ذلك (ولا يمكننا حتى لو أردنا ذلك) ، لذلك يجب أن تكون هناك قاعدة نحوية بحتة لتحديد المعنى المقصود.

حسنًا ، أرى وجهة نظرك حول الغموض المتعلق بمدى الخروج. في Clojure ، يتم تغليف التعبير بالكامل بـ #(...) لذا فهو لا لبس فيه.

في جوليا ، هل من الاصطلاح استخدام _ كقيمة لا تهتم؟ مثل x, _ = somfunc() إذا قام somefunc بإرجاع قيمتين وتريد الأولى فقط؟

لحل هذا ، أعتقد أننا سنحتاج إلى ماكرو باستخدام يشبه الاستيفاء:

some_obj |> @$(move($, 4, 5)) |> @$(scale($, 10)) |> display

لكن مرة أخرى ، أعتقد أن الأمر أصبح صاخبًا جدًا في تلك المرحلة ، ولا أعتقد أن @$(move($, 4, 5)) يعطينا أي شيء على الصيغة الحالية x -> move(x, 4, 5) ، وهي IMO أجمل وأكثر وضوحًا.

أعتقد أن هذا سيكون تطبيقًا جيدًا لماكرو infix. كما هو الحال مع # 4498 ، إذا كانت أي قاعدة تحدد الوظائف مثل اللاحمة المطبقة على وحدات الماكرو أيضًا ، فيمكن أن يكون لدينا ماكرو @-> أو @|> الذي سيكون له سلوك مؤشر الترابط.

يا ، تعجبني فكرة الماكرو infix ، على الرغم من أنه يمكن تقديم مشغل جديد لهذا الاستخدام بدلاً من وجود نظام كامل لوحدات الماكرو الداخلية. فمثلا،
some_obj ||> move($,4,5) ||> scale($, 10) |> disp
أو ربما احتفظ فقط بـ |> لكن لديك قاعدة
x |> f يتحول ضمنيًا إلى x |> f($) :
some_obj |> scale($,10) |> disp

أيها الناس ، كل شيء يبدو قبيحًا حقًا: |> ||> إلخ.
حتى الآن ، اكتشفت أن بناء جملة جوليا واضح جدًا لدرجة أن هذه الأشياء التي تمت مناقشتها أعلاه لا تبدو جميلة جدًا إذا ما قورنت بأي شيء آخر.

من المحتمل أن يكون هذا هو أسوأ شيء في Scala - لديهم الكثير من المشغلين مثل :: ،: ، << ، >> + :: وما إلى ذلك - إنه يجعل أي رمز قبيحًا وغير قابل للقراءة لشخص بدون بضعة أشهر من الخبرة في الاستخدام اللغة.

آسف لسماع أنك لا تحب العروض ، أنتون. سيكون من المفيد أن تقدم اقتراحًا بديلاً.

آسف ، أنا لا أحاول أن أكون قاسياً. ونعم - نقاد بدون مقترحات
غير مجدية.

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

تعجبني عبارة "عالم بناء اللغات" - إنها تبدو أكثر تعقيدًا من المبرمجين العدديين الذين سئموا ماتلاب.

أشعر أن كل لغة تقريبًا لديها طريقة لربط الوظائف - إما عن طريق التطبيق المتكرر . بلغات OO ، أو بناء جملة خاص فقط لهذا الغرض بلغات أكثر وظيفية (Haskell ، Scala ، Mathematica ، إلخ). تحتوي هذه اللغات الأخيرة أيضًا على صيغة خاصة لوسائل الدوال المجهولة ، لكنني لا أعتقد أن جوليا ستذهب حقًا إلى هناك.

سأكرر دعمنا لاقتراح سبنسر - تمت ترجمة x |> f(a) إلى f(x, a) ، بشكل مشابه جدًا لكيفية عمل كتل do (وهو يعزز السمة المشتركة التي تتمثل في الوسيطة الأولى لـ الوظيفة مميزة في جوليا لأغراض السكر النحوية). يُنظر بعد ذلك إلى x |> f على أنه اختصار لـ x |> f() . إنه بسيط ، ولا يقدم أي مشغلين جدد ، ويتعامل مع الغالبية العظمى من الحالات التي نريد تسلسل الوظائف لها ، وهو متوافق مع الإصدارات السابقة ، ويتناسب مع مبادئ تصميم Julia الحالية.

أعتقد أيضًا أن هذا هو أفضل اقتراح هنا ، والمشكلة الرئيسية هي أنه يبدو أنه يستبعد تحديد |> لأشياء مثل إعادة توجيه I / O أو أغراض أخرى مخصصة.

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

على نحو مماثل ، في Julia ، يمكن لمطور مكتبة بالفعل دعم التسلسل بـ |> خلال تحديد وظائفهم من وسيطات N لإرجاع دالة من وسيطة واحدة عند إعطاء وسيطات N-1 ، كما هو مذكور هنا

قد يبدو أن هذا يسبب مشاكل إذا كنت تريد أن تدعم وظيفتك عددًا متغيرًا من الوسائط ، لذا فإن وجود عامل يمكنه تنفيذ حشو الوسيطة سيكون أمرًا رائعًا.

JeffBezanson ، يبدو أنه يمكن تنفيذ هذا العامل إذا كانت هناك طريقة لعمل وحدات ماكرو infix. هل تعلم ما إذا كانت هناك مشكلة أيديولوجية في ذلك أم أنها لم تنفذ؟

في الآونة الأخيرة ، كان ~ مغلفًا بشكل خاص بحيث يقتبس من الحجج والمكالمات
الماكرو @~ افتراضيًا. يمكن جعل |> لعمل نفس الشيء.

بالطبع ، في غضون بضعة أشهر ، سيطلب شخص ما <| ليقوم بنفس الشيء ...

يوم الخميس ، 6 فبراير 2014 ، Spencer Russell [email protected]
كتب:

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

بشكل مشابه ، في Julia ، يمكن لمطور المكتبة بالفعل دعم التسلسل
مع |> من خلال تحديد وظائفهم من وسيطات N لإرجاع دالة
من وسيطة واحدة عند إعطاء وسيطات N-1 ، كما هو مذكور هنا https: //github.com/JuliaLang/julia/issues/5571#issuecomment -33408448

يبدو أن هذا يسبب مشاكل إذا كنت تريد أن تدعم وظيفتك
عدد متغير من args ، ومع ذلك ، فإن وجود عامل يمكن أن يؤدي
حشو الحجة سيكون لطيفًا.

JeffBezanson https://github.com/JeffBezanson ، يبدو أن هذا
يمكن تنفيذ عامل التشغيل إذا كانت هناك طريقة لعمل وحدات ماكرو infix. هل
تعرف ما إذا كانت هناك مشكلة أيديولوجية مع ذلك ، أم أنها لم تنفذ؟

-
قم بالرد على هذه الرسالة الإلكترونية مباشرة أو tHubhttps: //github.com/JuliaLang/julia/issues/5571#issuecomment -34374347
.

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

function move(obj::MyType, x, y, args...)
    # do stuff
    obj
end

move(args...) = obj::MyType -> move(obj, args...)

أعتقد أنه يمكن معالجة هذا السلوك بواسطة ماكرو @composable الذي سيتعامل مع الإعلان الثاني.

فكرة ماكرو infix جذابة بالنسبة لي في الحالة التي يتم فيها توحيدها مع إعلان وظائف infix ، والتي تمت مناقشتها في # 4498.

لماذا يعارض مبدعو جوليا بشدة السماح للكائنات باحتواء أساليبهم الخاصة؟ أين يمكنني قراءة المزيد عن هذا القرار؟ ما هي الأفكار والنظريات الكامنة وراء هذا القرار؟

meglio المكان الأكثر فائدة للأسئلة العامة هو القائمة البريدية أو علامة StackOverflow julia-lang . اطلع على حديث ستيفان وأرشيفات المستخدمين وقوائم المطورين للمناقشات السابقة حول هذا الموضوع.

مجرد رنين ، بالنسبة لي الشيء الأكثر بديهية هو استبدال بعض العناصر النائبة بـ
قيمة التعبير السابق في تسلسل الأشياء التي تحاول تكوينها ، على غرار ماكرو clojure as-> . إذا هذا:

<strong i="8">@as</strong> _ begin
    3+3
    f(_,y)
    g(_) * h(_,z)
end

سيتم توسيعه إلى:

g(f(3+3,y)) * h(f(3+3,y),z)

يمكنك التفكير في التعبير الموجود في السطر السابق "إسقاط" لملء فتحة الشرطة السفلية في السطر التالي.

لقد بدأت في رسم شيء صغير مثل هذا الربع الأخير في نوبة من المماطلة في أسبوع النهائيات.

يمكننا أيضًا دعم إصدار oneliner باستخدام |> :

<strong i="19">@as</strong> _ 3+3 |> f(_,y) |> g(_) * h(_,z)

porterjamesj ، أحب هذه الفكرة!

أنا موافق؛ هذا جميل جدًا وله عمومية جذابة.
في 7 فبراير 2014 ، الساعة 3:19 مساءً ، كتب "كيفن سكوير" [email protected] :

porterjamesj https://github.com/porterjamesj ، أحب هذه الفكرة!

قم بالرد على هذه الرسالة الإلكترونية مباشرة أو tHubhttps: //github.com/JuliaLang/julia/issues/5571#issuecomment -34497703
.

تعجبني فكرة

لاحظ أنه في Julia ، نظرًا لأننا لا نقوم بالكثير من النمط obj.method(args...) ، وبدلاً من ذلك نقوم بالنمط method(obj, args...) ، فإننا لا نميل إلى امتلاك طرق تعيد الكائنات التي تعمل عليها من أجل express الغرض من طريقة التسلسل. (وهو ما يفعله jQuery ، وهو أمر رائع في جافا سكريبت). لذلك نحن لا نحفظ الكثير من الكتابة هنا ، ولكن لغرض إعداد "الأنابيب" بين الوظائف ، أعتقد أن هذا أمر رائع حقًا.

بالنظر إلى أن clojure -> و ->> هي مجرد حالات خاصة لما سبق ، وشائعة إلى حد ما ، فمن المحتمل أن نتمكن من تنفيذها بسهولة أيضًا. على الرغم من أن السؤال عن ماذا نسميهم صعب بعض الشيء. ربما @threadfirst و @threadlast ؟

أنا أحب فكرة أن يكون هذا ماكرو أيضًا.

أليس من الأفضل أن يكون التوسيع ، على غرار المثال ، مثل

tmp = 3+3; tmp = f(tmp); return h(tmp, z)

لتجنب مكالمات متعددة لنفس العملية؟ (ربما كان هذا ضمنيًا بالفعل في فكرة porterjamesj )

اقتراح آخر: هل من الممكن أن تقوم الماكرو بتوسيع الاختصارات f إلى f(_) و f(y) إلى f(_,y) ؟ ربما سيكون الأمر أكثر من اللازم ، لكنني أعتقد أنه عندئذٍ لدينا خيار استخدام العنصر النائب فقط عند الحاجة ... (ومع ذلك ، يجب السماح بالاختصارات فقط في استدعاءات الوظائف وحدها ، وليس على تعبيرات مثل g(_) * h(_,z) أعلى)

cdsousa النقطة المتعلقة بتجنب المكالمات المتعددة فكرة جيدة. يستخدم تطبيق clojure روابط السماح المتسلسلة لتحقيق ذلك ؛ لست متأكدًا مما إذا كان بإمكاننا التخلص من هذا على الرغم من أنني لا أعرف ما يكفي عن أداء let .

إذن ، هل الماكرو @as يستخدم فواصل الأسطر و => كنقاط مقسمة لتحديد تعبير الاستبدال وما الذي يتم استبداله؟

let الأداء جيد ؛ الآن يمكن أن يكون بأسرع تعيين متغير إن أمكن ، وأيضًا سريعًا جدًا بخلاف ذلك.

ssfrr في تطبيق بفاصل الأسطر التي يدرجها المحلل اللغوي (ملحوظة ، أنا لا أفهم كل هذه

cdsousa :

اقتراح آخر: هل من الممكن أن تقوم الماكرو بتوسيع الاختصارات f إلى f(_) و f(y) إلى f(_,y)

f إلى f(_) أمر منطقي بالنسبة لي. بالنسبة للثاني ، أعتقد أن تحديد الموقع صراحةً هو الأفضل ، حيث يمكن للأشخاص العقلاء أن يجادلوا بأن إما f(_,y) أو f(y,_) أكثر طبيعية.

بالنظر إلى أن clojure -> و ->> هي مجرد حالات خاصة لما سبق ، وشائعة إلى حد ما ، فمن المحتمل أن نتمكن من تنفيذها بسهولة أيضًا. على الرغم من أن السؤال عن ماذا نسميهم صعب بعض الشيء. ربما @threadfirst و @threadlast ؟

أعتقد أن تحديد توضيح الموقع باستخدام f(_,y...) أو f(y..., _) يسمح للكود بأن يكون مفهومًا تمامًا. في حين أن بناء الجملة الإضافي (والمشغلين) منطقيين في Clojure ، ليس لدينا بالفعل عوامل تشغيل إضافية متاحة ، وأعتقد أن وحدات الماكرو الإضافية ستجعل الكود أقل وضوحًا بشكل عام.

إذن ، هل الماكرو @as يستخدم فواصل الأسطر و => كنقاط مقسمة لتحديد ما هو تعبير الاستبدال وما الذي يتم استبداله؟

أعتقد أنه من الطبيعي أكثر استخدام |> كنقطة انقسام ، لأنه مستخدم بالفعل في خطوط الأنابيب

فقط لكي تعرف ، هناك تنفيذ لماكرو مؤشر الترابط في Lazy.jl ، والذي يتيح لك كتابة ، على سبيل المثال:

@>> range() map(x->x^2) filter(iseven)

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

يمكنني أيضًا تنفيذ @as> في Lazy.jl إذا كان هناك اهتمام. يحتوي Lazy.jl الآن على ماكرو @as أيضًا.

يمكنك أيضًا القيام بشيء من هذا القبيل (على الرغم من استخدام صيغة تشبه Haskell) مع Monads.jl (ملاحظة: يجب تحديثها لاستخدام صيغة Julia الحالية). لكنني أظن أن إصدارًا متخصصًا لخيط المناقشة فقط يجب أن يكون قادرًا على تجنب عيوب الأداء التي ينطوي عليها النهج العام.

تبدو Lazy.jl حزمة لطيفة جدًا وتتم صيانتها بنشاط. هل هناك سبب مقنع لضرورة وجود هذا في Base؟

كيف ستعمل تسلسل الوظائف مع الوظائف التي تعيد قيمًا متعددة؟
ماذا ستكون نتيجة التسلسل على سبيل المثال:

function foo(a,b)
    a+b, a*b   # x,y respectively
end

و bar(x,z,y) = x * z - y be؟

ألن يتطلب تركيب جملة مثل bar(_1,z,_2) ؟

رمي في مثال آخر:

data = [2.255, 3.755, 6.888, 7.999, 9.001]

الطريقة النظيفة للكتابة: log(sum(round(data))) هو data|>round|>sum|>log
ولكن إذا أردنا عمل سجل للأساس 2 ، وأردنا التقريب إلى 3 كسور عشرية ،
ثم: يمكننا استخدام النموذج الأول فقط:
log(2,sum(round(data,3)))

لكن من الناحية المثالية نود أن نكون قادرين على القيام بما يلي:
data|>round(_,3)|>sum|>log(2,_)
(أو مشابه)

لقد صنعت نموذجًا أوليًا للطريقة التي أقترح أن يعمل بها.
https://github.com/oxinabox/Pipe.jl

إنه لا يحل نقطة gregid ، لكني أعمل على ذلك الآن.
كما أنه لا يعالج الحاجة إلى توسيع الحجج

إنه مشابه لوحدات ماكرو Lazy.jl للترابط @ one-more-minutes ولكنه يحتفظ بالرمز |> لسهولة القراءة (التفضيل الشخصي).

سأحولها ببطء إلى حزمة ، ربما ، في مرحلة ما

خيار آخر هو:

data |>   x -> round(x,2)  |> sum |>  x -> log(2,x)

على الرغم من أنه أطول من log(2,sum(round(data,2))) هذا الترميز يساعد أحيانًا في سهولة القراءة.

shashi هذا ليس سيئًا ، لم أفكر في ذلك ،
أعتقد بشكل عام مطول للغاية بحيث لا يمكن قراءته بسهولة

https://github.com/oxinabox/Pipe.jl الآن يحل مشكلة gregid .
على الرغم من أنك إذا طلبت كلاً من _[1] و _[2] فإنه يفعل ذلك عن طريق إجراء مكالمات متعددة إلى الاشتراك
الذي لست متأكدًا منه هو السلوك المرغوب فيه.

بصفتي شخصًا خارجيًا ، أعتقد أن مشغل خط الأنابيب سيستفيد من تكييف معاملة F # له.
من المؤكد أن F # بها كاري ، ولكن ربما يمكن عمل بعض السحر في النهاية الخلفية حتى لا تتطلب ذلك. مثل ، في تنفيذ المشغل ، وليس اللغة الأساسية.

هذا سيجعل [1:10] |> map(e -> e^2) ينتج عن [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] .

إذا نظرنا إلى الوراء ، ألمح ssfrr إلى هذا ، لكن الوسيطة obj في مثالهم سيتم منحها تلقائيًا إلى map كوسيطة ثانية في المثال الخاص بي ، وبالتالي إنقاذ المبرمجين من الاضطرار إلى تعريف وظائفهم إلى ادعمه.

ماذا تقترح أن هذا يعني؟

في 5 حزيران (يونيو) 2015 ، الساعة 5:22 مساءً ، كتب H-225 [email protected] :

بصفتي شخصًا خارجيًا ، أعتقد أن إحدى أفضل الطرق للقيام بذلك هي تكييف معاملة F # لها.
من المؤكد أن F # بها كاري ، ولكن ربما يمكن عمل بعض السحر في النهاية الخلفية حتى لا تتطلب ذلك. مثل ، في تنفيذ المشغل ، وليس اللغة الأساسية.

هذا سيجعل من [1:10] |> الخريطة (e -> e ^ 2) ينتج عنها [1 ، 4 ، 9 ، 16 ، 25 ، 36 ، 49 ، 64 ، 81 ، 100].

أنا شخصياً أعتقد أنه لطيف وواضح دون الإسهاب.

من الواضح أنه يمكن للمرء أن يكتب نتيجة = خريطة (sqr ، [1:10]) ، لكن لماذا يمتلك مشغل خط الأنابيب على الإطلاق؟
ربما هناك شيء مفقود؟

-
قم بالرد على هذا البريد الإلكتروني مباشرةً أو قم بعرضه على GitHub.

تضمين التغريدة
بشكل أساسي ، اجعل المشغل يعمل مثل:

  • x |> y(f) = y(x, f)
  • x |> y(f) = y(f, x)

ربما يكون لديك نمط واجهة تستخدمه أي وظيفة مع المشغل تأخذ البيانات لتعمل على أنها الوسيطة الأولى أو الأخيرة ، اعتمادًا على أي مما سبق يتم تحديده ليكون هذا النمط.
لذلك ، بالنسبة للدالة map كمثال ، سيكون map إما map(func, data) أو map(data, func) .

هل هذا أوضح؟

تبدو Lazy.jl حزمة لطيفة جدًا وتتم صيانتها بنشاط. هل هناك سبب مقنع لضرورة وجود هذا في Base؟

أعتقد أن هذا هو السؤال المهم هنا.

السبب الذي قد يكون من المرغوب فيه في القاعدة هو ضعفين:

1.) قد نرغب في تشجيع خطوط الأنابيب على أنها طريقة جوليان - يمكن تقديم الحجج التي تجعلها أكثر قابلية للقراءة
2.) أشياء مثل Lazy.jl و FunctionalData.jl و Pipe.jl الخاصة بي تتطلب ماكروًا لتغليف التعبير الذي تعمل عليه - مما يجعله أقل قابلية للقراءة.

أشعر أن الإجابة قد تكمن في امتلاك Infix Macros.
وتعريف |> على هذا النحو.

لست متأكدًا من أن |> ، (أو ابن عمهم كتلة do) ينتمي إلى جوهر على الإطلاق.
لكن الأدوات غير موجودة لتعريفها خارج المحلل اللغوي.

تبدو القدرة على الحصول على هذا النوع من بناء جملة خطوط الأنابيب رائعة جدًا. هل يمكن إضافة ذلك إلى Base ، على سبيل المثال x |> y(f) = y(f, x) الجزء الذي يمكن أن يستخدمه Lazy.j و FunctionalData.jl و Pipe.jl؟ : +1:

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

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

أنا أفهم ما تقصده رغم ذلك ؛ سيكون أكثر انتظامًا إذا كان لديك صيغة استدعاء دالة واحدة لكل شيء. أنا شخصياً أعتقد أنه من الأفضل تسهيل كتابة كود [معقد] يمكن فهمه بسهولة. من المؤكد أنك يجب أن تتعلم بناء الجملة وما تعنيه ، ولكن ، IMHO |> ليس من الصعب فهمه من كيفية استدعاء دالة.

tkelman كنت أنظر إليها من وجهة نظر مختلفة. من الواضح أن هناك أشخاصًا يفضلون هذا النمط من البرمجة. أستطيع أن أرى أنه ربما ترغب في الحصول على نمط ثابت لشفرة المصدر إلى Base ، لكن هذا يتعلق فقط بإضافة دعم المحلل اللغوي لأسلوب البرمجة المفضل لديهم _the_ تطبيقات Julia. هل يريد جوليان حقًا محاولة إملاء أو خنق شيء يجده الآخرون مفيدًا؟
لقد وجدت أن عناصر ربط الأنابيب معًا مفيدة جدًا في Unix ، لذا على الرغم من أنني لم أستخدم لغة برمجة تمكنها في اللغة من قبل ، إلا أنني سأعطيها على الأقل ميزة الشك.

لدينا |> كمشغل أنابيب وظيفي ، ولكن هناك قيود على التنفيذ لكيفية القيام به حاليًا مما يجعله بطيئًا جدًا في الوقت الحالي.

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

حسنًا ، شكرًا ، كنت أتحدث عن تعليق

لكن الأدوات غير موجودة لتعريفها خارج المحلل اللغوي.

هل من المفهوم ما الذي يمكن عمله لإزالة قيود التنفيذ التي أشرت إليها؟

يمكن تنفيذ بعض الاقتراحات السابقة بجعل |> يوزع وسيطاته كماكرو بدلاً من وظيفة. تم إهمال معنى أنابيب كائن الأمر السابق |> ، لذلك قد يتم تحرير هذا بالفعل للقيام بشيء مختلف ، يأتي 0.5-dev.

ومع ذلك ، فإن هذا الخيار يذكرني كثيرًا بالتحليل الخاص لـ ~ والذي أشعر أنه خطأ لأسباب ذكرتها في مكان آخر.

الإعراب ~ مجرد جنون ، إنها دالة في الأساس. يبدو استخدام _ ، _1 ، _2 _ أكثر_ معقولية (خاصة إذا رفعت إذا تم تحديد هذه المتغيرات في مكان آخر في النطاق). لا يزال حتى لدينا وظائف مجهولة أكثر كفاءة يبدو أنها لن تعمل ...

تم تنفيذه بجعل |> تحليل وسيطاته كماكرو بدلاً من كدالة

ما لم تفعل ذلك!

الإعراب ~ مجرد جنون ، إنها دالة في الأساس

إنه عامل أحادي لإصدار أحادي. Infix binary ~ parses كماكرو ، المرجع https://github.com/JuliaLang/julia/issues/4882 ، والذي أعتقد أنه استخدام غريب لمشغل ascii (https://github.com/ JuliaLang / Julia / pull / 11102 # issuecomment-98477891).

تضمين التغريدة

إذن لدينا تركيبان ، لكن أحدهما أقل منطقية في حالة MIMO.

3 النحوية. نوعا من.
إدخال الأنابيب ، استدعاء الوظيفة العادية وكتل العمل.
قابل للنقاش حتى 4 ، لأن وحدات الماكرو تستخدم اصطلاحًا مختلفًا أيضًا.


لي،
أمر القراءة (أي من اليسار إلى اليمين) == ترتيب التطبيق يجعل سلاسل وظائف SISO أكثر وضوحًا.

أفعل الكثير من التعليمات البرمجية مثل (باستخدام iterators.jl و pipe.jl):

  • loaddata(filename) |> filter(s-> 2<=length(s)<=15, _) |> take!(150,_) |> map(eval_embedding, _)
  • results |> get_error_rate(desired_results, _) |> round(_,2)

بالنسبة إلى SISO ، هذا أفضل (لتفضيل شخصي) ، أما بالنسبة إلى MIMO فهو ليس كذلك.

يبدو أن جوليا قد استقرت بالفعل على وجود العديد من الطرق الصحيحة للقيام بالأشياء.
وهو أمر لست متأكدًا بنسبة 100٪ أنه أمر جيد.

كما قلت ، أود نوعًا ما مثل نقل كتل Pipe and Do خارج اللغة الرئيسية.

يحتوي Do-block على عدد قليل جدًا من حالات الاستخدام المفيدة للغاية ، ولكنه أزعجني قليلاً أنه يتعين عليهم استخدام الإدخال الأول كوظيفة ، ولا يتلاءم دائمًا مع فلسفة الإرسال المتعدد (ولن يكون أيًا من الباندا / D style UFCS مع postfix data.map(f).sum() ، أعلم أنه شائع لكنني لا أعتقد أنه يمكن دمجه بشكل فعال مع إرسال متعدد).

من المحتمل أن يتم إهمال الأنابيب في وقت قريب جدًا ، وتركها للحزم لاستخدامها في DSL مثل Pipe.jl.

يبدو أن جوليا قد استقرت بالفعل على وجود العديد من الطرق الصحيحة للقيام بالأشياء.
وهو أمر لست متأكدًا بنسبة 100٪ أنه أمر جيد.

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

تضمين التغريدة
لم أجد بعد سببًا واضحًا لعدم إدراجها في اللغة ، أو في الواقع في الحزم "الأساسية". [على سبيل المثال: قاعدة]
أنا شخصياً أعتقد أن جعل ماكرو |> هو الحل.
شيء ما _ مثل هذا ربما؟ (أنا لست مبرمجًا محترفًا في جوليا!)

macro (|>) (x, y::Union(Symbol, Expr))
    if isa(y, Symbol)
        y = Expr(:call, y) # assumes y is callable
    end
    push!(y.args, x)
    return eval(y)
end

تحت إصدار Julia v0.3.9 ، لم أتمكن من تعريفه مرتين - مرة برمز ومرة ​​مع تعبير ؛ إن فهمي [المحدود] لـ Union هو أن هناك تأثرًا في الأداء من استخدامه ، لذلك أعتقد أنه سيكون شيئًا يجب تصحيحه في رمز مثال لعبتي.

بالطبع ، هناك مشكلة في استخدام بناء الجملة لهذا الغرض.
على سبيل المثال ، لتشغيل ما يعادل log(2, 10) ، عليك كتابة @|> 10 log(2) ، وهو أمر غير مرغوب فيه هنا.
ما أفهمه هو أنه يجب أن تكون قادرًا على تمييز الوظائف / وحدات الماكرو بطريقة ما على أنها "غير قابلة للإصلاح" ، كما كانت ، بحيث يمكنك كتابتها على هذا النحو: 10 |> log(2) . (صح إذا كان خطأ!)
مثال مخادع ، أعرف. لا أستطيع التفكير في فكرة جيدة الآن! =)

من الجدير أيضًا الإشارة إلى مجال واحد لم أقم بتغطيته في المثال الخاص بي ...
لذلك على سبيل المثال:

julia> for e in ([1:10], [11:20] |> zip) println(e) end
(1,11)
(2,12)
(3,13)
(4,14)
(5,15)
(6,16)
(7,17)
(8,18)
(9,19)
(10,20)

مرة أخرى - مثال مفتعل ، ولكن نأمل أن تحصل على النقطة!
لقد قمت ببعض العبث ، لكن حتى كتابة هذا المقال لم أتمكن من فهم كيفية تنفيذ ذلك بنفسي.

يرجى الاطلاع على https://github.com/JuliaLang/julia/issues/554#issuecomment -110091527 و # 11608.

في 9 حزيران (يونيو) 2015 ، الساعة 9:37 مساءً ، كتب H-225 [email protected] :

لم أجد بعد سببًا واضحًا لعدم إدراجها في اللغة

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

السؤال يجب أن يكون بـ "لماذا؟" بدلا من "لماذا لا؟"

+ 1_000_000

في الواقع.
انظر إلى هذه المدونة المشهورة إلى حد
تبدأ كل ميزة بـ -100 نقطة.
يحتاج إلى إجراء تحسين كبير حتى يستحق إضافة إلى اللغة.

FWIW ، خضع Pyret (http://www.pyret.org/) لهذه المناقشة بالضبط قبل بضعة أشهر. تدعم اللغة تدوين "cannonball" والذي كان يعمل في الأصل إلى حد كبير بالطريقة التي يقترحها الأشخاص باستخدام |> . في Pyret ،

[list: 1, 2, 3, 5] ^ map(add-one) ^ filter(is-prime) ^ sum() ^ ...

لذلك ، تم إلغاء رمز المدفع لإضافة وسيطات إلى الوظائف.

لم يمض وقت طويل قبل أن يقررا أن بناء الجملة هذا محير للغاية. لماذا يتم استدعاء sum() بدون أي وسيطات؟ في النهاية ، اختاروا بديلًا أنيقًا للكاري:

[list: 1, 2, 3, 5] ^ map(_, add-one) ^ filter(_, is-prime) ^ sum() ^ ...

تتميز هذه الميزة بأنها أكثر وضوحًا وتبسيط عامل التشغيل ^ إلى وظيفة بسيطة.

نعم ، هذا يبدو أكثر منطقية بالنسبة لي. كما أنه أكثر مرونة من الكاري.

تضمين التغريدة هل تقصد أن تقول أكثر مرونة من التسلسل (ليس كاري)؟ بعد كل شيء ، كان حل Pyret هو استخدام الكاري ببساطة ، وهو أكثر عمومية من التسلسل.

ربما ، إذا قمنا بتعديل بناء الجملة |> قليلاً (لا أعرف حقًا مدى صعوبة التنفيذ ، فربما يتعارض مع | و > ) ، يمكن أن تضع شيئًا مرنًا وقابل للقراءة.

تحديد شيء مثل

foo(x,y) = (y,x)
bar(x,y) = x*y

سيكون لدينا:

randint(10) |_> log(_,2) |> sum 
(1,2) |_,x>  foo(_,x)   |x,_>   bar(_,2) |_> round(_, 2) |> sum |_> log(_, 2)

بمعنى آخر ، سيكون لدينا عامل مثل |a,b,c,d> حيث سيحصل a و b و c و d على القيم المرجعة لـ التعبير الأخير (بالترتيب) واستخدمه في العناصر النائبة داخل التعبير التالي.

إذا لم تكن هناك متغيرات داخل |> فستعمل كما تعمل الآن. يمكننا أيضًا تعيين معيار جديد: f(x) |> g(_, 1) سيتم إرجاع جميع القيم بمقدار f(x) وربطها بالعنصر النائب _ .

samuela ، ما قصدته هو أنه من خلال الكاري يمكنك فقط حذف الحجج _ ، يمكنك حذف أي وسيطات والحصول على وظيفة مجهولة. على سبيل المثال ، إذا أعطيت f(x,y) مع الكاري ، يمكنك القيام بـ f(x) للحصول على وظيفة تعمل بـ y -> f(x,y) ، ولكن مع الشرطات السفلية ، يمكنك عمل f(x,_) لنفس الشيء ولكن أيضًا اعمل f(_,y) لتحصل على x -> f(x,y) .

بينما تعجبني صيغة الشرطة السفلية ، ما زلت غير راضٍ عن أي إجابة مقترحة للسؤال حول مقدار التعبير المحيط الذي "يلتقطه".

ماذا تفعل إذا كانت الدالة ترجع نتائج متعددة؟ هل سيتعين عليه تمرير tuple إلى الموضع _؟ أو يمكن أن يكون هناك بناء جملة لتقسيمه بسرعة؟ قد يكون سؤال غبي ، إذا كان الأمر كذلك ، عفوا!

@ StefanKarpinski آه ، أرى ما تعنيه. متفق عليه.

ScottPJones الجواب الواضح هو السماح
http://scrambledeggsontoast.github.io/2014/09/28/needle-announce/

simonbyrne هذا يبدو أسوأ من البرمجة في Fortran IV على بطاقات مثقوبة ، كما فعلت في شبابي الذي

Simonbyrne هذا رائع. تنفيذ ذلك كسلسلة ماكرو سيكون مشروع GSoC مذهلاً.

لماذا يتم استدعاء sum () دون أي حجج؟

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

simonbyrne ألا تعتقد أنه يمكن القيام بذلك بطريقة لا لبس فيها؟ إذا كان الأمر كذلك ، فهذا شيء أعتقد أنه يستحق كسره (الترميز الحالي do ) ، إذا كان من الممكن جعله أكثر منطقية ، وأكثر عمومية ، ومتسقًا مع التسلسل.

simonbyrne نعم ، أوافق تمامًا. أتفهم الدافع وراء الترميز الحالي do لكنني أشعر بقوة أنه لا يبرر الجمباز النحوي.

samuela بخصوص الخريطة (f ، _) مقابل الخريطة فقط (f). أوافق على أن بعض إزالة الصغائر السحرية ستكون مربكة ، لكنني أعتقد أن الخريطة (و) شيء يجب أن يكون موجودًا. لن يتطلب الأمر والسكر فقط أضف طريقة بسيطة للتخطيط.
على سبيل المثال

map(f::Base.Callable) = function(x::Any...) map(f,x...) end

على سبيل المثال ، تأخذ map دالة ثم تعيد دالة تعمل على الأشياء القابلة للتكرار (أكثر أو أقل).

بشكل عام ، أعتقد أننا يجب أن نميل نحو الدوال التي لها طرق "ملائمة" إضافية ، بدلاً من نوع من الاصطلاح الذي يقوم فيه |> دائمًا بتعيين البيانات إلى الوسيطة الأولى (أو ما شابه ذلك).

في نفس السياق يمكن أن يكون هناك ملف

type Underscore end
_ = Underscore()

واتفاقية عامة مفادها أن الدالات يجب / يمكن أن تحتوي على طرق تأخذ الشرطة السفلية في وسيطات معينة ، ثم تعيد الدوال التي تتطلب وسيطات أقل. أنا أقل اقتناعًا بأن هذه ستكون فكرة جيدة ، حيث سيحتاج المرء إلى إضافة توابع 2 ^ n لكل دالة تأخذ n وسيطات. لكنها طريقة واحدة. أتساءل عما إذا كان من الممكن ألا تضطر إلى إضافة العديد من الطرق بشكل صريح ولكن بدلاً من ذلك ربط طريقة البحث ، بحيث إذا كانت أي وسيطات من النوع Underscore ، فسيتم إرجاع الوظيفة المناسبة.

على أي حال ، أعتقد بالتأكيد أن وجود نسخة من الخريطة والتصفية التي تأخذ فقط إمكانية الاستدعاء وتعيد الاستدعاء أمر منطقي ، الشيء الذي يحتوي على السطر السفلي قد يكون قابلاً للتطبيق وقد لا يكون كذلك.

تضمين التغريدة
أتخيل أن x |> map(f, _) => x |> map(f, Underscore()) => x |> map(f, x) ، كما تقترح ، سيكون أبسط طريقة لتنفيذ map(f, _) ، أليس كذلك؟ - هل لديك فقط _ ليكون كيانًا خاصًا يمكنك البرمجة من أجله؟

على الرغم من ذلك ، فأنا غير متأكد مما إذا كان ذلك سيكون أفضل من استنتاجه تلقائيًا من قبل جوليا - على الأرجح باستخدام بناء الجملة |> بدلاً من الاضطرار إلى برمجته بنفسك.

أيضًا ، فيما يتعلق باقتراحك لـ map - أحبه نوعًا ما. في الواقع ، بالنسبة إلى |> سيكون ذلك مفيدًا جدًا. على الرغم من أنني أتصور أنه سيكون أكثر بساطة أفضل لمجرد تنفيذ الاستدلال التلقائي لل x |> map(f, _) => x |> map(f, x) بدلا من ذلك؟

تضمين التغريدة لم أفكر في الأمر هكذا تمامًا.

لا شيء قلته سيكون مرتبطًا بـ |> بأي شكل من الأشكال. ما قصدته بخصوص _ سيكون على سبيل المثال إضافة طرق إلى < على هذا النحو:

<(_::Underscore, x) = function(z) z < x end
<(x, _::Underscore) = function(z) x < z end

لكن مرة أخرى أعتقد أن هذا سيكون مؤلمًا ما لم تكن هناك طريقة لإضافة الأساليب المناسبة تلقائيًا.

مرة أخرى ، الشيء الذي يحتوي على الشرطة السفلية منفصل عن إضافة طريقة الراحة للتعيين كما هو موضح أعلاه. أعتقد أن كلاهما يجب أن يتواجد بشكل أو بآخر.

patrickthebold مثل هذا النهج مع نوع محدد من قبل المستخدم

f(_, x, y) = ...
f(x, _, y) = ...
f(_, _, y) = ...
...

سيكون مزعجًا جدًا ، ناهيك عن عدم الأناقة.

أيضًا ، اقتراحك مع map ، أفترض أنه يوفر بنية حل بديلة لـ map(f) مع وظائف أساسية مثل map و filter ولكن بشكل عام يعاني من نفس مسألة التعقيد مثل نهج التأكيد اليدوي. على سبيل المثال ، بالنسبة إلى func_that_has_a_lot_of_args(a, b, c, d, e) يجب أن تمر بعملية شاقة تتمثل في كتابة كل كلمة "كاري" ممكنة

func_that_has_a_lot_of_args(a, b, c, d, e) = ...
func_that_has_a_lot_of_args(b, c, d, e) = ...
func_that_has_a_lot_of_args(a, b, e) = ...
func_that_has_a_lot_of_args(b, d, e) = ...
func_that_has_a_lot_of_args(a, d) = ...
...

وحتى لو فعلت ذلك ، فستظل تواجه قدرًا سخيفًا من الغموض عند استدعاء الوظيفة: هل يشير func_that_has_a_lot_of_args(x, y, z) إلى التعريف حيث x=a,y=b,z=c أو x=b,y=d,z=e ، إلخ. ؟ سوف تميز جوليا بينهما بمعلومات نوع وقت التشغيل ولكن بالنسبة للمبرمج العادي الذي يقرأ الكود المصدري ، سيكون الأمر غير واضح تمامًا.

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

تضمين التغريدة
هل يمكنك توضيح ما تعنيه بعبارة "اسحبها للخارج لإنشاء لامدا"؟ - انا فضولي. أنا أيضا تساءلت كيف يمكن تنفيذ ذلك.

تضمين التغريدة
آه لقد فهمت. من المفترض أنه يمكنك بعد ذلك استخدام شيء مثل هذا: filter(_ < 5, [1:10]) => [1:4] ؟
أنا شخصياً أجد أن قراءة filter(e -> e < 5, [1:10]) أسهل ؛ أكثر اتساقًا - معنى أقل خفيًا ، على الرغم من أنني أعطيك ، فهو أكثر إيجازًا.

ما لم يكن لديك مثال حيث يضيء حقًا؟

تضمين التغريدة

أيضًا ، سأفترض أن اقتراحك مع الخريطة يوفر بناء جملة حلًا للخريطة (f) مع الوظائف الأساسية مثل الخريطة والفلتر ، ولكنه بشكل عام يعاني من نفس مشكلة التعقيد مثل نهج التسطير اليدوي.

لم أكن أقترح أن يتم ذلك بشكل عام ، فقط مقابل map و filter ، وربما في بعض الأماكن الأخرى التي يبدو واضحًا فيها. بالنسبة لي ، هذه هي الطريقة التي يجب أن يعمل بها map : خذ دالة وأعد دالة. (من المؤكد أن هذا ما يفعله هاسكل).

سيكون مزعجًا جدًا ، ناهيك عن عدم الأناقة.

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

@ H-225 نعم الشيء الذي تسطير أسفل السطر هو مجرد راحة نحوية. لست متأكدًا من مدى شيوعها ، لكن سكالا تمتلكها بالتأكيد. أنا شخصياً أحبها ، لكنني أعتقد أنها مجرد واحدة من تلك الأشياء الأنيقة.

@ H-225 حسنًا ، في هذه الحالة أعتقد أن أحد الأمثلة المقنعة وذات الصلة سيكون تسلسل الوظائف. بدلا من الاضطرار إلى الكتابة

[1, 2, 3, 5]
  |> x -> map(addone, x)
  |> x -> filter(isprime, x)
  |> sum
  |> x -> 3 * x
  |> ...

يمكن للمرء أن يكتب ببساطة

[1, 2, 3, 5]
  |> map(addone, _)
  |> filter(isprime, _)
  |> sum
  |> 3 * _
  |> ...

أجد نفسي دون علمي أستخدم صيغة الشرطة السفلية (أو بعض المتغيرات البسيطة) باستمرار في اللغات التي تدعمها وأدرك فقط مدى فائدة ذلك عند الانتقال إلى العمل بلغات لا تدعمه.

على حد علمي ، يوجد حاليًا ما لا يقل عن 3.5 مكتبة / نهج تحاول معالجة هذه المشكلة في Julia: Julia's builtin |> function و Pipe.jl و Lazy.jl و 0.5 لـ Julia's builtin do تدوين مشابه في الروح. عدم تقريع أي من هذه المكتبات أو الأساليب ، ولكن يمكن تبسيط العديد منها بشكل كبير إذا كانت جوليا تدعم استخدام التسطير السفلي.

samuela إذا كنت ترغب في اللعب بتطبيق هذه الفكرة ، يمكنك تجربة FunctionalData.jl ، حيث

<strong i="7">@p</strong> map [1,2,3,4] addone | filter isprime | sum | times 3 _

يوضح الجزء الأخير كيفية توجيه الإدخال إلى المعامل الثاني (الافتراضي هو الوسيطة الأولى ، وفي هذه الحالة يمكن حذف _ ). ردود الفعل موضع تقدير كبير!


تحرير: تتم إعادة كتابة ما سبق ببساطة إلى:

times(3, sum(filter(map([1,2,3,4],addone), isprime)))

الذي يستخدم FunctionalData.map والتصفية بدلاً من Base.map والتصفية. الاختلاف الرئيسي هو ترتيب الوسيطة ، والاختلاف الثاني هو اصطلاح الفهرسة (انظر المستندات). في أي حال ، يمكن استخدام Base.map ببساطة عن طريق عكس ترتيب الوسيطة. @p هي قاعدة بسيطة لإعادة الكتابة (من اليسار إلى اليمين يصبح من الداخل إلى الخارج ، بالإضافة إلى دعم الكي البسيط: <strong i="17">@p</strong> map data add 10 | showall يصبح

showall(map(data, x->add(x,10)))

قد يقدم هاك شيئًا مثل هذا: https://github.com/facebook/hhvm/issues/6455. إنهم يستخدمون $$ وهو خارج الطاولة بالنسبة لـ Julia ( $ مثقل بالفعل بشكل زائد).

FWIW ، أنا حقًا أحب حل Hack لهذا.

يعجبني أيضًا ، الحجز الرئيسي الخاص بي هو أنني ما زلت أحب تدوين lambda terser الذي قد يستخدم _ للمتغيرات / الفتحات وسيكون من الجيد التأكد من عدم تعارضها.

لا يمكن للمرء استخدام __ ؟ ما هي صيغة لامدا التي تفكر فيها؟ _ -> sqrt(_) ؟

بالتأكيد نستطيع. هذه البنية تعمل بالفعل ، إنها تتعلق بشكل أكبر بالبنية التي لا تتطلب السهم ، بحيث يمكنك كتابة شيء على غرار map(_ + 2, v) ، المشكلة الحقيقية هي مقدار التعبير المحيط _ ينتمي إلى.

ألا تمتلك Mathematica نظامًا مشابهًا للحجج المجهولة؟ كيف
فهم يتعاملون مع نطاق حدود تلك الحجج؟
يوم الثلاثاء 3 نوفمبر 2015 الساعة 9:09 صباحًا Stefan Karpinski [email protected]
كتب:

بالتأكيد نستطيع. هذه البنية تعمل بالفعل ، إنها تتعلق أكثر بالبنية التي
لا يتطلب السهم ، بحيث يمكنك كتابة شيء على غرار السطور
من الخريطة (_ + 2 ، v) ، المشكلة الحقيقية هي مقدار المناطق المحيطة
التعبير الذي ينتمي إليه _.

-
قم بالرد على هذا البريد الإلكتروني مباشرةً أو قم بعرضه على GitHub
https://github.com/JuliaLang/julia/issues/5571#issuecomment -153383422.

https://reference.wolfram.com/language/tutorial/PureFunctions.html ، عرض
# الرمز هو ما كنت أفكر فيه.
في الثلاثاء ، 3 نوفمبر 2015 الساعة 9:34 صباحًا ، كتب جوناثان مالود [email protected] :

ألا تمتلك Mathematica نظامًا مشابهًا للحجج المجهولة؟ كيف
فهم يتعاملون مع نطاق حدود تلك الحجج؟
يوم الثلاثاء 3 نوفمبر 2015 الساعة 9:09 صباحًا Stefan Karpinski [email protected]
كتب:

بالتأكيد نستطيع. هذه البنية تعمل بالفعل ، إنها تتعلق أكثر بالبنية التي
لا يتطلب السهم ، بحيث يمكنك كتابة شيء على غرار السطور
من الخريطة (_ + 2 ، v) ، المشكلة الحقيقية هي مقدار المناطق المحيطة
التعبير الذي ينتمي إليه _.

-
قم بالرد على هذا البريد الإلكتروني مباشرةً أو قم بعرضه على GitHub
https://github.com/JuliaLang/julia/issues/5571#issuecomment -153383422.

تستخدم Mathematica & لتحديدها.

بدلاً من القيام بشيء عام مثل بناء جملة lambda أقصر (والذي يمكن أن يأخذ تعبيرًا تعسفيًا ويعيد دالة مجهولة) ، يمكننا التغلب على مشكلة المحدد من خلال قصر التعبيرات المقبولة على استدعاءات الوظيفة ، والمتغيرات / الفتحات المقبولة على معلمات كاملة. هذا من شأنه أن يعطينا صيغة كاري نظيفة جدًا متعددة المعلمات على _ يحل محل المعلمات بالكامل ، فقد يكون بناء الجملة بسيطًا وبديهيًا ولا لبس فيه. map(_ + 2, _) إلى x -> map(y -> y + 2, x) . من المحتمل أن تكون معظم تعبيرات الاستدعاء غير الوظيفية التي قد ترغب في استخدام lambdafy أطول منها وأكثر ملاءمة لـ -> أو do أي حال. أعتقد أن المقايضة بين قابلية الاستخدام مقابل العمومية تستحق العناء.

durcan ، هذا يبدو واعدًا - هل يمكنك توضيح القاعدة قليلاً؟ لماذا يبقى أول _ داخل وسيطة map بينما الثاني يستهلك التعبير map بالكامل؟ لست واضحًا بشأن ما تعنيه عبارة "حصر التعبيرات المقبولة على استدعاءات الوظائف" ، ولا ما يعنيه "قصر المتغيرات / الفتحات المقبولة على معلمات كاملة" ...

حسنًا ، أعتقد أنني حصلت على القاعدة ، بعد أن قرأت بعضًا من وثائق ديلان هذه ، لكن يجب أن أتساءل عن وجود عمل map(_ + 2, v) لكن map(2*_ + 2, v) لا يعمل.

هناك أيضًا عمل صعب للغاية وهذا يعني أن _ + 2 + _ سيعني (x,y) -> x + 2 + y بينما _ ⊕ 2 ⊕ _ سيعني y -> (x -> x + 2) + y لأن + و * هم العاملون الوحيدون الذين يقومون حاليًا بالتحليل كمكالمات دالة متعددة الوسائط بدلاً من العمليات الترابطية الزوجية. الآن ، يمكن المجادلة بأن هذا التناقض يجب إصلاحه ، على الرغم من أن ذلك قد يستلزم أن يكون لدى الموزع رأي حول العوامل المترابطة وأيها ليست كذلك ، وهذا يبدو سيئًا. لكنني أزعم أن أي مخطط يتطلب معرفة ما إذا كان المحلل يوزع a + b + c كمكالمة دالة واحدة أو مكالمة متداخلة قد يكون مشكوكًا فيه إلى حد ما. ربما يجب التعامل مع تدوين اللاحمة بشكل خاص؟ لكن لا ، هذا يبدو مريبًا أيضًا.

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

2*_ |> 2+_ |> map(_, v)

على أي حال ، لا أعتقد أن مشكلة infix يجب أن تقف في طريق الحصول على خيار مجاني نظيف محدد. سيساعد حقًا في معظم استدعاءات الوظائف العادية ، وهي نوع من المشكلة المطروحة. إذا كنت تريد ، يمكنك الحصول على محدد اختياري للمساعدة في حل هذا الغموض المحدد (هنا أنا أسرق & لهذا الدور):

_ ⊕ 2 ⊕ _    # y -> (x -> x + 2) + y
_ ⊕ 2 ⊕ _ &  # (y , x) -> x + 2 + y

هذا هو أفضل عرض حتى الآن ، لكني لم يتم بيعي بالكامل. من الواضح تمامًا ما يحدث عندما تكون استدعاءات الوظائف صريحة ، ولكن أقل وضوحًا عندما يتم تضمين استدعاءات الوظائف من خلال بناء جملة infix.

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

نعم ، أنا أحب ذلك باستثناء الشيء infix. قد يكون هذا الجزء قابل للإصلاح.

حسنًا ، قد يكون التجفيف في موضع infix خطأً في بناء الجملة:

map(+(*(2, _), 2), v)      # curry is OK syntax, but obviously not what you wanted
map(2*_ + 2, v)            # ERROR: syntax: infix curry requires delimitation
map(2*_ + 2 &, v)          # this means what we want
map(*(2,_) |> +(_,2), v)   # as would this

قد يكون أيضًا تحذيرًا على ما أعتقد.

وصف هذا بالكلام ، يبدو لي محيرًا وخاطئًا.

بالتأكيد ، هذا يشبه إلى حد كبير تطبيق الوظيفة الجزئية (الذي يصبح اختيارياً لامدا مجهولة المصدر) أعتقد. إذا وضعنا جانبا أي أفكار؟

أفكر على غرار شيء مثل هذا:

  • إذا ظهر _ بمفرده كأي من وسيطات تعبير استدعاء الوظيفة ، فسيتم استبدال استدعاء الوظيفة بتعبير دالة مجهول يأخذ العديد من الوسائط مثل الدالة التي تحتوي على وسيطات _ ، والتي يكون جسمها هو التعبير الأصلي باستخدام وسيطات _ استبدالها بوسائط lambda بالترتيب.
  • إذا ظهر _ مكان آخر ، فسيتم استبدال التعبير المحيط الذي يصل إلى مستوى أسبقية السهم أو أي أقواس محيطة به دون تضمينه (ولكن ليس الأقواس المربعة أو الأقواس المتعرجة) ، بوظيفة مجهولة تأخذ العديد من الوسيطات مثل _ في هذا التعبير ، ونصه هو التعبير الأصلي مع استبدال مثيلات _ بوسائط lambda بالترتيب.

أمثلة:

  • f(_, b)x -> f(x, b)
  • f(a, _)x -> f(a, x)
  • f(_, _)(x, y) -> f(x, y)
  • 2_^2x -> 2x^2
  • 2_^_(x, y) -> 2x^y
  • map(_ + 2, v)map(x -> x + 2, v)
  • map(2_ + 2, v)map(x -> 2x + 2, v)
  • map(abs, _)x -> map(abs, x)
  • map(2_ + 2, _)x -> map(y -> 2y + 2, x)
  • map(2_ - _, v, w)map((x, y) -> 2x - y, v, w)
  • map(2_ - _, v, _)x -> map((y, z) -> 2y - z, v, x)
  • map(2_ - _, _, _)(x, y) -> map((z, w) -> 2z - w, x, y)
  • _x -> x
  • map(_, v)x -> map(x, v)
  • map((_), v)map(x -> x, v)
  • f = _f = x -> x
  • f = 2_f = x -> 2x
  • x -> x^_x -> y -> x^y
  • _ && _(x, y) -> x && y
  • !_ && _(x, y) -> !x && y

المكان الوحيد الذي يبدأ فيه هذا الأمر في أن يصبح مشبوهًا هو الشرطية - هذه الأمثلة تصبح غريبة نوعًا ما.

لا يزال هذا الأمر صعبًا وغير مبدئي إلى حد ما وهناك حالات زاوية ، لكنني أذهب إلى مكان ما.

يبدو لي هذا فكرة سيئة حقًا ، فكل بناء الجملة تقريبًا في Julia مألوف جدًا إذا كنت قد استخدمت لغات برمجة أخرى. الأشخاص الذين يبحثون في تركيب السكر مثل هذا لن يكون لديهم أي فكرة عما يحدث من أجل "فائدة" إنقاذ اثنين من الشخصيات.

أمثلة تجعلني أقل سعادة:

  • 2v[_]x -> 2v[x] (جيد)
  • 2f(_)2*(x -> f(x)) (ليس جيدًا)
  • _ ? "true" : "false"(x -> x) ? "true" : "false"
  • _ ? _ : 0(x -> x) ? (y -> y) : 0

أعتقد أن شيئًا أعمق يحدث هنا - هناك فكرة عن المواضع النحوية التي يكون فيها كائن الوظيفة - منطقيًا - وتريد التوسع إلى أقرب واحد منها. الموضع الكلاسيكي الذي "يريد" كائن دالة هو حجة لوظيفة أخرى ، ولكن الجانب الأيمن من التخصيص هو مكان آخر يمكن أن يقال إنه "يريد" كائن دالة (أو ربما يكون أكثر دقة أن نقول إنه يفضل تأخذ وظيفة لتكون جسم وظيفة).

ربما ، ولكن يمكن طرح نفس الحجة (وقد تم) حول بناء جملة do-block ، والذي أعتقد ، بشكل عام ، أنه كان ناجحًا ومفيدًا للغاية. يرتبط هذا ارتباطًا وثيقًا ببناء الجملة الأفضل للتوجيه. كما أنها ليست غير مسبوقة - يستخدم Scala _ بطريقة مماثلة ، و Mathematica يستخدم # بالمثل.

أعتقد أنه يمكنك أيضًا إثبات أن الخيار _ غير المألوف_ لـ
إرسال متعدد بدلاً من مشغل نقطي أحادي الإرسال بشكل أساسي
يُلزم القرار بالحصول على بناء جملة موجز للحجج الضمنية لـ
استعادة طلب عمليات قسم عمليات الطوارئ التي يعرفها الأشخاص.

يوم الثلاثاء ، 17 نوفمبر 2015 الساعة 12:09 مساءً Stefan Karpinski [email protected]
كتب:

ربما ، ولكن نفس الحجة يمكن (وقد تم) حول كتلة do-block
بناء الجملة ، والذي أعتقد ، بشكل عام ، كان ناجحًا ومفيدًا للغاية.
يرتبط هذا ارتباطًا وثيقًا ببناء الجملة الأفضل للتوجيه. إنه ليس كذلك
هذا غير مسبوق - يستخدم Scala _ بطريقة مماثلة ، ويستخدم Mathematica #
بالمثل.

-
قم بالرد على هذا البريد الإلكتروني مباشرةً أو قم بعرضه على GitHub
https://github.com/JuliaLang/julia/issues/5571#issuecomment -157437223.

يوجد هذا أيضًا في C ++ مع حلول مكتبة متعددة في Boost على وجه الخصوص ، والتي تستخدم _1, _2, _3 كوسيطات (على سبيل المثال _1(x, y...) = x ، _2(x, y, z...) = y إلخ) ، القيد هو أن تكون قادرًا على اتصل على سبيل المثال fun(_1) مقابل x -> fun(x) ، fun يجب أن يكون متوافقًا بشكل صريح مع المكتبة (عادةً عبر مكالمة ماكرو ، لجعل fun يقبل "نوع lambda "كمعامل).

أود حقًا أن يتوفر تدوين لامدا المقتضب في جوليا.
بخصوص مشكلة 2f(_) desugaring to 2*(x -> f(x)) : هل سيكون من المنطقي تعديل القواعد على غرار "إذا كانت القاعدة الأولى تنطبق ، على سبيل المثال f(_) ، ثم إعادة- تقييم القواعد بشكل متكرر باستخدام f(_) يلعب دور _ . سيسمح هذا أيضًا على سبيل المثال f(g(_))x -> f(g(x)) ، مع "قاعدة الأقواس" التي تسمح بسهولة توقف عند المستوى المطلوب ، على سبيل المثال f((g(_)))f(x->g(x)) .

يعجبني اسم "تدوين لامدا المقتضب" كثيرًا لهذا الغرض. (أفضل بكثير من الكاري).

أنا أفضل حقًا صراحة _1 ، _2 ، _3 إذا اجتازت Lambdas متعددة الوسائط. بشكل عام ، غالبًا ما أجد أن إعادة استخدام الأسماء المتغيرة في نفس النطاق قد يكون أمرًا محيرًا ... ويبدو أن وجود _ يكون x و y في _ نفس التعبير_ أمرًا محيرًا للغاية.

لقد وجدت نفس scala _ -syntax تسبب في القليل من الالتباس (انظر جميع استخدامات _ في scala ).

إلى جانب ذلك ، غالبًا ما تريد القيام بما يلي:

x -> f(x) + g(x)

أو ما شابه ، وأعتقد أنني سأفاجأ إذا لم ينجح ما يلي:

f(_) + g(_)

قد ترغب أيضًا في تبديل ترتيب الوسائط:

x, y -> f(y, x)
f(_2, _1)  # can't do with other suggested _ syntax

أعتقد أنه سيكون من الجيد أن تسمح الصيغة بترقيم الوسيطات المجهول الصريح ( _1 ، _2 ، _3 ... إلخ) ، لكن المشكلة الرئيسية لا تزال قائمة : متى بالضبط تقوم بترقية دالة مطبقة جزئيًا إلى لامدا مقتضب؟ وما هو بالضبط جسم لامدا؟ من المحتمل أن أخطئ في جانب أن أكون صريحًا (مع محدد) بدلاً من استخدام نوع من قواعد الترويج المعقدة بشكل ضمني. ماذا يجب

foo(_1, _1 + _2  + f(_1, v1) + g(_2, v3), _3 * _2, v2) + g(_4, v4) +
 f(_2, v2) + g(_3, v5) + bar(_1, v6)

يعني بالضبط؟ استخدام المحدد (سأستخدم λ ) الأشياء أكثر وضوحًا إلى حد ما:

λ(foo(_1, λ(_1 + _2)  + λ(f(_1, v1) + g(_2, v3)), _3 * _2, v2) + g(_4, v4)) + 
λ(f(_2, v2) + g(_3, v5) + bar(_1, v6))

من الواضح أن هذا هو MethodError: + has no method matching +(::Function, ::Function) ، لكن على الأقل يمكنني معرفة ذلك من طريقة كتابته.

أعتقد أن StefanKarpinski قد يكون في

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

ربما لا تكون مقتضبة بدرجة كافية ، ولكن ماذا عن إصدار بادئة من -> يلتقط وسائط _ ؟ على سبيل المثال

(-> 2f(_) + 1)

أعتقد أن صيغة البادئة يجب أن يكون لها أسبقية منخفضة. قد يسمح هذا في الواقع بتجاهل الأقواس في بعض الحالات ، على سبيل المثال

map(->_ + 1, x)

الآن أنا أعبث في تنفيذ https://github.com/JuliaLang/julia/issues/5571#issuecomment -157424665

كماكرو ، يقوم هذا بتحويل كل هذه التكرارات في السطر.
الجزء الصعب هو تطبيق الأسبقية.

ربما لن أنتهي منه خلال الـ 12 ساعة القادمة لأنه حان الوقت هنا
(ربما في الـ 24 القادمة ، لكن قد أضطر للذهاب بعيدًا)
على أي حال ، بمجرد أن يتم ذلك يمكننا اللعب به.

واحد غريب يخرج من https://github.com/JuliaLang/julia/issues/5571#issuecomment -157424665

  • f(_,_)x,y -> f(x,y) (هذا معقول)
  • f(_,2_) → ؟؟

    • f(_,2_)x,y -> f(x,2y) (معقول)

    • f(_,2_)x-> f(x,y->2y) (ما أعتقد أن القاعدة تقترحه ، وما ينتج النموذج الأولي الخاص بي)

لكني لست متأكدًا من أنني أحقق ذلك.

لذلك هذا هو النموذج الأولي الخاص بي.
http://nbviewer.ipython.org/gist/oxinabox/50a1e17cfb232a7d1908

في الواقع ، فشل بالتأكيد في بعض الاختبارات.

لا يمكن التفكير في وضع الأقواس في طبقة AST الحالية - غالبًا ما يتم حلها (دائمًا؟) بالفعل.

لا يزال يكفي اللعب مع أعتقد

بعض القواعد من magrittr في R قد تكون مفيدة:

إذا بدأت السلسلة بـ. ، إنها وظيفة مجهولة:

. %>% `+`(1)

هي نفس الوظيفة (x) x + 1

هناك طريقتان للتسلسل:
1) التسلسل عن طريق الإدراج كأول وسيط ، وكذلك إلى أي نقاط تظهر.
2) التسلسل فقط إلى النقاط التي تظهر.

الوضع الافتراضي هو الوضع 1. ومع ذلك ، إذا ظهرت النقطة بمفردها كوسيطة للوظيفة التي يتم ربطها بالسلاسل ، فإن ماغريتر ينتقل إلى الوضع 2 لتلك الخطوة في السلسلة.

وبالتالي

2 %>% `-`(1) 

هو 2 - 1 ،

و

1 %>% `-`(2, . )

هو أيضًا 2 - 1

يمكن أيضًا تحديد الوضع 2 عن طريق الالتفاف بين قوسين:

2 %>% { `-`(2, . - 1) }

سيكون هو نفسه 2 - (2-1).

أيضًا مجرد ملاحظة مفادها أن القدرة على التبديل بذكاء بين الوضع 1 والوضع 2 تحل تمامًا تقريبًا مشكلة جوليا ليست متسقة للغاية بشأن وجود الحجة التي من المحتمل أن يتم ربطها بالسلاسل في الموضع الأول. لقد نسيت أيضًا أن أشير إلى أن الأقواس يمكن أن تسمح بتقييم جزء من التعليمات البرمجية. هذا مثال من دليل magrittr:

قزحية٪>٪
{
n <- عينة (1:10 ، الحجم = 1)
H <- رأس (. ، ن)
T <- ذيل (. ، ن)
rbind (H ، T)
}٪>٪
ملخص

هذه مجرد فكرة نصفية في الوقت الحالي ، لكنني أتساءل عما إذا كانت هناك طريقة يمكننا من خلالها حل مشكلات "lambda المقتضب" و "المتغير الوهمي" في نفس الوقت عن طريق تعديل مُنشئ Tuple بحيث تُرجع القيمة المفقودة lambda التي تُرجع Tuple بدلاً من Tuple؟ لذلك ، فإن (_, 'b', _, 4) سيعود (x, y) -> (x, 'b', y, 4) .

بعد ذلك ، إذا قمنا بتغيير دلالات استدعاء الدوال بمهارة بحيث يعني foo(a, b) "تطبيق foo على Tuple (a, b) أو إذا كانت الوسيطة دالة ، فقم بتطبيق foo إرجاع foo(_, b, c)(1) يعادل apply(foo, ((x) -> (x, b, c))(1)) .

أعتقد أن هذا لا يزال لا يحل مشكلة تدوين infix ، لكنني شخصيًا سأكون سعيدًا باستخدام lambdas المقتضب التي تعمل فقط مع استدعاءات الوظائف بين قوسين. بعد كل شيء، 1 + _ يمكن دائما إعادة كتابة +(1, _) عند الضرورة القصوى.

jballanc ومع ذلك ، فإن بناء tuple وتطبيق الوظيفة هما مفهومان مختلفان تمامًا. على الأقل ، ما لم يكن فهمي لدلالات جوليا معيبًا بشكل خطير.

samuela ما قصدته هو أن foo(a, b) يعادل foo((a, b)...) . وهذا يعني أن الحجج الخاصة بوظيفة ما يمكن اعتبارها مفاهيميًا على أنها Tuple ، حتى لو لم يتم إنشاء Tuple في الممارسة.

لقد حاولت قراءة هذا النقاش ، لكن الوقت طويل جدًا بالنسبة لي لتتبع كل ما قيل - آسف جدًا إذا كنت أكرر هنا أكثر من اللازم.

أود فقط إجراء تصويت لجعل |> مكملاً لـ do "السحر". بقدر ما أستطيع أن أرى ، فإن أسهل طريقة للقيام بذلك هي السماح لها بمعنى ذلك

3 |> foo == foo(3) # or foo() instead of just foo, but it would be nice if the parentheses were optional
3 |> foo(1) == foo(1, 3)
3 |> foo(1,2) == foo(1,2,3)

بمعنى آخر ، يفعل a |> f(x) للوسيطة _last_ ما يفعله f(x) do; a; end بـ _first_. هذا سيجعله متوافقًا على الفور مع map ، filter ، all ، any et. al. ، دون إضافة تعقيد تحديد نطاق المعلمات _ ، وبالنظر إلى بناء الجملة الحالي do لا أعتقد أنه يضع عبئًا مفاهيميًا غير معقول على قراء الكود.

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

هذا هو الماكرو @>> من https://github.com/MikeInnes/Lazy.jl.

malmaud لطيف! يعجبني أن هذا ممكن بالفعل: د

ومع ذلك ، فإن الاختلاف في إمكانية القراءة بين هذين المتغيرين كبير حقًا:

# from Lazy.jl
@> x g f(y, z)

# if this became a first-class feature of |>
x |> g |> f(y, z)

أعتقد أن مشكلة قابلية القراءة الرئيسية هي أنه لا توجد أدلة مرئية لمعرفة مكان الحدود بين التعبيرات - ستؤثر المسافات الموجودة في x g و g f(x, بشكل كبير على سلوك الكود ، لكن المساحة في f(x, y) لن.

نظرًا لأن @>> موجود بالفعل ، ما مدى جدوى إضافة هذا السلوك إلى |> في 0.5؟

(لا أرغب في إنفاق الكثير من الطاقة على تدوين المشغل ، لذلك لا ترفض هذا الاقتراح فقط بشأن مسألة الترميز. ومع ذلك ، يمكننا ملاحظة أن "الأنابيب" تبدو فكرة طبيعية لهذا (راجع المصطلح "خط أنابيب التجميع") ، وهذا على سبيل المثال يستخدم F # بالفعل |> لهذا ، على الرغم من أنه يحتوي بالطبع على دلالات مختلفة قليلاً لأن وظائف F # تختلف عن وظائف Julia.)

يا ، بالتأكيد أوافق على جبهة القراءة. لن يكون من الصعب تقنيًا جعل |> يتصرف كما تصف في 0.5 ، إنه مجرد سؤال تصميم.

وبالمثل ، سيكون من الممكن جعل وظائف تحليل الماكرو لـ Lazy.jl @>> متسلسلة بواسطة |> .

جلالة الملك. سأبدأ العمل على العلاقات العامة لـ Lazy.jl بعد ذلك ، لكن هذا لا يعني أنني أود ألا يكون هذا في 0.5 :) لا أعتقد أنني أعرف ما يكفي عن محلل Julia و كيفية تغيير سلوك |> للمساعدة في ذلك ، على الرغم من ذلك ، ما لم أحصل على بعض التوجيه المكثف.

لا أعتقد أنني ذكرت في هذا الموضوع ، لكن لدي حزمة تسلسل أخرى ، ChainMap.jl. وهو دائمًا ما يستبدل بعلامة _ ، ويُدرج شرطيًا في الوسيطة الأولى. يحاول أيضًا دمج التعيين. راجع https://github.com/bramtayl/ChainMap.jl

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

الحزم

النماذج غير الحزمة

ذات صلة:


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

تم التحديث: 2020-04-20

هذه تجربة نظام كتابة أكثر من كونها محاولة فعلية لتنفيذ تطبيق جزئي ، ولكن إليك تجربة غريبة: https://gist.github.com/fcard/b48513108a32c13a49a387a3c530f7de

الاستعمال:

include("partial_underscore_generated.jl")
using GeneratedPartial

const sub = partialize(-)
sub(_,2)(1) == 1-2
sub(_,_)(1,2) == 1-2
sub(_,__)(1)(2) == 1-2
sub(__,_)(2)(1) == 1-2 #hehehe

# or
<strong i="8">@partialize</strong> 2 Base.:+ # evily inserts methods in + and allows partializations for 2 arguments
(_+2)(1) == 1+2

# fun:
sub(1+_,_)(2,3) == sub(1+2,3)
sub(1+_,__)(2)(3) == sub(1+2,3)
(_(1)+_)(-,1) == -1+1

# lotsafun:
appf(x::Int,y::Int) = x*y
appf(f,x) = f(x)
<strong i="9">@partialize</strong> 2 appf

appf(1+_,3)(2) == appf(1+2,3)
appf(?(1+_),3) == appf(x->(1+x), 3)
appf(?sub(_,2),3) == appf(x->x-2,3) # I made a method *(::typeof(?),::PartialCall), what of it!!?

# wooooooooooooooooooooooooooooooooo
const f = sub
f(_,f(_,f(_,f(_,f(_,f(_,f(_,f(_,f(_,_)))))))))(1,2,3,4,5,6,7,8,9,10) == f(1,f(2,f(3,f(4,f(5,f(6,f(7,f(8,f(9,10)))))))))
f(_,f(__,f(___,f(____,f(_____,f(______,f(_______,f(________,f(_________,__________)))))))))(1)(2)(3)(4)(5)(6)(7)(8)(9)(10) == f(1,f(2,f(3,f(4,f(5,f(6,f(7,f(8,f(9,10)))))))))

# this answers Stefan's concern (which inspired me to make this hack in the first place)
#
#    const pmap = partialize(map)
#    map(f(_,a),   v) == map(x->f(x,a), v)
#    pmap(?f(_,a), v) == map(x->f(x,a), v)
#    pmap(f(_,a),  v) == x->map(f(x,a), v)
#
# it adds a few other issues, of course...


بالتأكيد ليس اقتراحًا للتنفيذ ، لكني أجد أنه من الممتع اللعب به ، وربما يمكن لشخص ما الحصول على نصف فكرة جيدة منه.

ملاحظة: نسيت أن أذكر أن @partialize يعمل أيضًا مع قيم حرفية لمجموعة عدد صحيح ونطاقات:

<strong i="15">@partialize</strong> 2:3 Base.:- # partialized for 2 and 3 arguments!

(_-_-_)(1,2,3) == -4
(_-_+_)(1,2,3) == +2

حسنًا ، لقد كنت أفكر في تكوين الوظيفة ، وعلى الرغم من أن IMO واضح في حالة SISO ، أعتقد أنني كنت أفكر في كيفية استخدام Julia's MISO (شبه MIMO؟) في كتل التعليمات البرمجية الصغيرة المتسلسلة.

أنا أحب REPL. كثير. إنه لطيف ، إنه رائع ، فهو يتيح لك تجربة مثل MATLAB أو Python أو الصدفة. أحب أخذ رمز من حزمة أو ملف ونسخه ولصقه في REPL ، حتى سطور متعددة من التعليمات البرمجية. يقوم REPL بتقييم ذلك في كل سطر ويظهر لي ما يحدث.

تقوم أيضًا بإرجاع / تحديد ans بعد كل تعبير. يعرف كل مستخدم MATLAB ذلك (رغم أن هذه حجة سيئة في هذه المرحلة!). ربما شاهده معظم مستخدمي Julia / استخدموه من قبل. أستخدم ans في المواقف الفردية التي ألعب فيها بشيء من الحكمة ، مدركًا أنني أريد إضافة خطوة أخرى إلى ما كتبته أعلاه. أنا لا أحب أن استخدامه مدمر نوعًا ما ، لذلك أميل إلى تجنبه عندما يكون ذلك ممكنًا ، لكن _كل_ اقتراح هنا يتعامل مع فترات الإرجاع لخطوة واحدة فقط من التكوين.

بالنسبة لي، _ كونها السحرية هو مجرد _odd_، لكنني أدرك أن الكثير من الناس قد يختلفون. لذلك ، إذا كنت أريد نسخ ولصق كود من الحزم في REPL ومشاهدته ، وإذا كنت أرغب في بناء جملة لا يبدو سحريًا ، فقد أقترح:

<strong i="12">@repl_compose</strong> begin
   sin(x)
   ans + 1
   sqrt(ans)
end

إذا كانت الوظيفة ترجع مخرجات متعددة ، فيمكنني إدراج ans[1] ، ans[2] ، إلخ في السطر التالي. إنه يناسب بدقة المستوى الفردي لنموذج التكوين بالفعل ، نموذج Julia's MISO ، وهو بالفعل _بنية جوليا قياسية جدًا _ ، فقط ليس في الملفات.

من السهل تنفيذ الماكرو - ما عليك سوى تحويل Expr(:block, exprs...) إلى Expr(:block, map(expr -> :(ans = $expr), exprs) (أيضًا let ans في البداية ، وربما يكون هناك إصدار يصنع وظيفة متقطعة تتطلب إدخال أو شيء من هذا القبيل؟). لن يكون من المفترض أن تعيش في القاعدة (على الرغم من أن REPL مدمج في جوليا ، وهو نوع يتوافق مع ذلك).

على أي حال ، فقط وجهة نظري! كان هذا خيطًا طويلًا لم ألق نظرة عليه منذ وقت طويل!

يقوم أيضًا بإرجاع / تحديد الجواب بعد كل تعبير. يعرف كل مستخدم MATLAB ذلك (رغم أن هذه حجة سيئة في هذه المرحلة!).

في الواقع ، الحجة الأخرى هي أنه إذا تم استخدام _ في تسلسل الوظائف ، فيجب أن تُرجع REPL أيضًا _ بدلاً من ans (بالنسبة لي ، سيكون هذا كافيًا للإزالة السحر").

هناك قدر معقول من سابقة استخدام _ كـ "قيمة it" في اللغات. بالطبع ، هذا يتعارض مع الفكرة المقترحة لاستخدام _ كاسم يتجاهل التعيينات ولللامدا الصغيرة.

أنا متأكد من أن هذا موجود في مكان ما في Lazy.jl كـ <strong i="5">@as</strong> and begin ...

تم تجاهل فكرة استخدام . للتسلسل في وقت مبكر من المحادثة ، ولكن لها تاريخ طويل من الفائدة وستقلل من منحنى التعلم للمتبنين من اللغات الأخرى. سبب أهمية ذلك بالنسبة لي هو

type Track
  hit::Array{Hit}
end
type Event
  track::Array{Track}
end

event.track[12].hit[43]

يعطيني النتيجة 43 للمسار الثاني عشر لحدث ما عندما يكون track و hit مصفوفات بسيطة ، لذلك

event.getTrack(12).getHit(43)

يجب أن تعطيني نفس الشيء إذا كان يجب أن يتم تقديمها بشكل ديناميكي. لا أريد أن أقول

getHit(getTrack(event, 12), 43)

يزداد الأمر سوءًا كلما تعمقت. نظرًا لأن هذه وظائف بسيطة ، فإنها تجعل الحجة أوسع من تلك المتعلقة بتسلسل الوظائف (a la Spark).

أكتب هذا الآن لأنني علمت للتو عن سمات روست ، والتي يمكن أن تكون حلاً جيدًا في جوليا للأسباب نفسها. مثل جوليا ، لدى Rust بيانات فقط structs (Julia type ) ، لكن بعد ذلك لديهم أيضًا impl لوظائف الربط باسم struct . بقدر ما أستطيع أن أقول ، إنه سكر نحوي نقي ، لكنه يسمح بالتدوين النقطي الذي وصفته أعلاه:

impl Event {
  fn getTrack(&self, num: i32) -> Track {
    self.track[num]
  }
}

impl Track {
  fn getHit(&self, num: i32) -> Track {
    self.track[num]
  }
}

التي يمكن أن تكون في جوليا

impl Event
  function getTrack(self::Event, num::Int)
    self.track[num]
  end
end

impl Track
  function getHit(self::Track, num::Int)
    self.hit[num]
  end
end

لا تقوم الصيغة المقترحة أعلاه بأي تفسير لـ self : إنها مجرد وسيطة دالة ، لذلك يجب ألا يكون هناك تعارض مع الإرسال المتعدد. إذا كنت تريد إجراء تفسير أدنى self ، فيمكنك جعل نوع الوسيطة الأولى ضمنيًا ، بحيث لا يضطر المستخدم إلى كتابة ::Event و ::Track في كل وظيفة ، ولكن الشيء الجيد في عدم القيام بأي تفسير هو أن "الطرق الثابتة" هي مجرد وظائف في impl التي لا تحتوي على self . (يستخدمها Rust لمصانع new .)

على عكس Rust ، فإن جوليا لديها تسلسل هرمي على types . يمكن أن يكون لها أيضًا تسلسل هرمي مماثل على impls لتجنب تكرار الكود. يمكن إنشاء OOP القياسي بجعل البيانات type وطريقة impl متشابهة تمامًا ، لكن هذا الانعكاس الصارم ليس ضروريًا وفي بعض الحالات غير مرغوب فيه.

هناك نقطة ثابتة واحدة مع هذا: لنفترض أنني قمت بتسمية الوظائف track و hit في impl ، بدلاً من getTrack و getHit ، بحيث تتعارض مع المصفوفات track و hit في type . هل سيعيد event.track المصفوفة أو الدالة؟ إذا لك على الفور استخدامها بوصفها وظيفة، التي يمكن أن تساعد على إزالة الغموض، ولكن types يمكن أن تعقد Function الأشياء أيضا. ربما مجرد تطبيق قاعدة شاملة: بعد النقطة ، تحقق أولاً من impl المقابل ، ثم تحقق من type المقابل؟

عند التفكير الثاني ، لتجنب وجود "حزمتين" لما هو نفس الشيء من الناحية النظرية ( type و impl ) ، ماذا عن هذا:

function Event.getTrack(self, num::Int)
  self.track[num]
end

لربط الدالة getTrack بمثيلات من النوع Event مثل ذلك

myEvent.getTrack(12)

ينتج عن نفس الرمز الثانوي مثل الوظيفة المطبقة على (myEvent, 12) ؟

الجديد هو بناء جملة typename-dot-functionname بعد الكلمة الرئيسية function وكيف يتم تفسيرها. لا يزال هذا يسمح بالإرسال المتعدد ، مثل Python self إذا كانت الوسيطة الأولى هي نفس النوع المرتبط به (أو اليسار ضمنيًا ، كما هو مذكور أعلاه) ، ويسمح بـ "طريقة ثابتة" إذا الوسيطة الأولى غير موجودة أو مكتوبة بشكل مختلف عن النوع المرتبط به.

jpivarski هل هناك سبب في اعتقادك أن بناء جملة النقطة (الذي ، من خلال قراءة هذا الموضوع ، له الكثير من العيوب) أفضل من بعض التركيبات الأخرى التي تسمح بالتسلسل؟ ما زلت أعتقد أن إنشاء شيء مثل do ولكن بالنسبة للوسيطة الأخيرة ، مدعومًا بشكل من أشكال تركيب الأنابيب (على سبيل المثال ، |> ) سيكون أفضل طريقة للمضي قدمًا:

event |> getTrack(12) |> getHit(43)

السبب الرئيسي الذي يجعلني أرى أن شيئًا مثل نهج Rust يمكن أن يكون أفضل هو أنه يستخدم بشكل فعال الجانب الأيسر كمساحة اسم للوظائف ، لذلك قد تتمكن من القيام بأشياء مثل parser.parse دون التعارض مع الموجود جوليا Base.parse وظيفة. سأؤيد تقديم كل من اقتراح Rust وأنابيب نمط Hack.

tlycken هذا بناء جملة غامض ، اعتمادًا على الأسبقية.
قد يكون تذكر دقة |> vs call أمرًا محيرًا ، لأنه لا يعطي أي تلميحات حقًا.
(ولم يتم اقتراح العديد من الخيارات الأخرى).

يعتبر

foo(a,b) = a+b
foo(a) = b -> a-b

2 |> foo(10) == 12   #Pipe Precedence > Call Precedence 
2 |> foo(10) == 8     #Pipe Precedence < Call Precedence   

oxinabox أنا في الواقع لا أقترح أن يكون "مجرد" عامل عادي ، بل عنصر بناء جملة للغة ؛ 2 |> foo(10) desugars إلى foo(10, 2) حد كبير بنفس الطريقة foo(10) do x; bar(x); end desugars إلى foo(x -> bar(x), 10) . يشير هذا إلى أسبقية الأنبوب على أسبقية المكالمة (والتي أعتقد أنها أكثر منطقية على أي حال).

فقط فيما يتعلق بموضوع بناء الجملة ، فإن . أقل تشويشًا بصريًا وبالتأكيد أكثر معيارًا من |> . يمكنني كتابة سلسلة من الوظائف بطلاقة مفصولة بـ . بدون مسافات (حرف واحد لكل منهما) ويمكن لأي شخص قراءتها ؛ باستخدام |> ، سأضطر إلى إضافة مسافات (أربعة أحرف لكلٍّ منها) وسيكون ذلك بمثابة ضربة سريعة مرئية لمعظم المبرمجين. إن التشابه مع برنامج shell scripting | رائع ، لكن لا يمكن التعرف عليه على الفور بسبب > .

هل أقرأ هذا الموضوع بشكل صحيح ، حيث أن الحجة ضد النقطة هي أنه من الغموض ما إذا كان يجب الحصول على بيانات عضو من type أو وظيفة عضو من impl (اقتراحي الأول) أو الوظيفة مساحة الاسم (اقتراحي الثاني)؟ في الاقتراح الثاني ، يجب تحديد الوظائف المحددة في مساحة اسم الوظيفة التي تم إنشاؤها بواسطة type _after_ يتم تحديد type ، لذلك يمكن أن يرفض حجب بيانات العضو هناك.

إضافة نقطتي مساحة الاسم (اقتراحي الثاني) و |> سيكون أمرًا جيدًا بالنسبة لي ؛ كلاهما مختلفان في الغرض والتأثير ، على الرغم من حقيقة أنه يمكن استخدامهما في التسلسل بطلاقة ومع ذلك ، فإن |> كما هو موضح أعلاه ليس متماثلًا تمامًا مع do ، نظرًا لأن do يتطلب الوسيطة التي تملأها لتكون دالة. إذا كنت تقول event |> getTrack(12) |> getHit(43) ، فإن |> ينطبق على الوظائف غير ( Events و Tracks ).

إذا كنت تقول event |> getTrack(12) |> getHit(43) ، فإن |> ينطبق على الوظائف غير ( Events و Tracks ).

في الواقع ، لا - إنها تنطبق على تمائم الوظيفة _ الموجودة على يمينها_ عن طريق إدخال المعامل الأيسر الخاص بها باعتباره الوسيطة الأخيرة لاستدعاء الوظيفة. event |> getTrack(12) هو getTrack(12, event) بسبب ما كان على اليمين ، وليس بسبب ما كان على اليسار.

يجب أن يعني هذا أ) الأسبقية على استدعاءات الوظائف (نظرًا لأنها إعادة كتابة للمكالمة) ، و ب) طلب التطبيق من اليسار إلى اليمين (لجعله getHit(43, getTrack(12, event)) بدلاً من getHit(43, getTrack(12), event) ) .

لكن توقيع getTrack's هو

function getTrack(num::Int, event::Event)

لذلك إذا قام event |> getTrack(12) بإدراج event في الوسيطة الأخيرة getTrack's ، فسيتم وضع Event في الوسيطة الثانية ، وليس Function . لقد جربت للتو المعادل بـ do والوسيطة الأولى ، واشتكت Julia 0.4 من أن الوسيطة يجب أن تكون دالة. (ربما لأن do event end يتم تفسيره على أنه دالة تُرجع event ، بدلاً من event نفسها.)

يبدو لي أن تسلسل الوظائف يمثل مشكلة منفصلة عن الكثير مما تتم مناقشته حول بناء جملة النقطة ( . ). على سبيل المثال ، jpivarski ، يمكنك بالفعل إنجاز الكثير مما ذكرته من Rust في Julia بدون أي ميزات جديدة:

type TownCrier
  name::AbstractString
  shout::Function

  function TownCrier(name::AbstractString)
    self = new(name)
    self.shout = () -> "HELLO, $(self.name)!"
    self
  end
end

tc = TownCrier("Josh")
tc.shout()                                #=> "HELLO, Josh!"
tc.name = "Bob"
tc.shout()                                #=> "HELLO, Bob!"

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

andyferris لقد كنت أستخدم Python وأحب _ حقًا الإشارة إلى نتيجة التعبير السابق. لا يعمل داخل الوظائف رغم ذلك. سيكون من الرائع لو تمكنا من تشغيله في أي مكان: داخل كتل البداية والوظائف وما إلى ذلك.

أعتقد أن هذا يمكن أن يحل محل السلاسل تمامًا. لا يترك أي غموض حول ترتيب الحجج. فمثلا،

begin
    1
    vcat(_, 2)
    vcat(3, _)
end

# [3, 1, 2]

كما ذكر MikeInnes ، هذا متاح بالفعل بـ @_ في Lazy.jl (وعلى الرغم من أنه لم يعمل بهذه الطريقة في الأصل ، يستخدم ChainMap.jl أيضًا هذا النوع من السلاسل الآن).

نأمل أن يكون هذا قادرًا على العمل مع الاندماج النقطي ، على الأقل داخل الكتل

begin
    [1, 2, 3]
    .+(_, 2)
    .*(_, 2)
    .-(10, _)
end

أو باستخدام بناء الجملة @chain_map ،

begin
    ~[1, 2, 3]
    +(_, 2)
    *(_, 2)
    -(10, _)
end

توجد حاليًا طريقة لتسلسل الوظائف مع الكائنات إذا تم تحديد الوظيفة داخل المُنشئ. على سبيل المثال ، وظيفة Obj.times:

type Obj
    x
    times::Function
    function Obj(x)
       this = new(x)
       this.times =  (n) -> (this.x *= n; this)
       this
    end
end

>>>Obj(2).times(3)
Obj(6,#3)

ماذا عن تنفيذ وظائف الأعضاء (الوظائف الخاصة) المحددة خارج تعريف النوع. على سبيل المثال ، ستتم كتابة الوظيفة Obj.times على النحو التالي:

member function times(this::Obj, n)
     this.x *= n
     return this
end

>>>Obj(2).times(3)
Obj(6,#3)

حيث member هي كلمة أساسية خاصة لوظائف الأعضاء.
وظائف الأعضاء لديها حق الوصول إلى بيانات الكائن. لاحقًا ، سيتم استدعاؤها باستخدام النقطة بعد متغير الكائن.
الفكرة هي إعادة إنتاج سلوك الوظائف المحددة داخل المُنشئين باستخدام وظائف "العضو" المحددة خارج تعريف النوع.

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

لقد قرأت هذا وبعض القضايا ذات الصلة ، إليكم اقتراحي:

التسلسل الأساسي :
in1 |> function1
مثل: in1 |> function1(|>)

in2 |> function2(10)
مثل: in2 |> function2(|>,10)

المزيد من التسلسل:
in1 |> function1 |> function2(10,|>)

تفريع ودمج السلسلة:
فرع مرتين بفروع out1 ، out2 :
function1(a) |out1>
function2(a,b) |out2>

استخدم الفرع out1 و out2 :
function3(|out1>,|out2>)

ماذا عن الكسل؟
هل نحتاج إلى شيء مثل اتفاقية function!(mutating_var) ؟
للوظائف البطيئة يمكننا استخدام function?() ...

وباستخدام المسافة البادئة بشكل صحيح ، من السهل تتبع تبعيات البيانات بصريًا على الرسم البياني للمكالمات المرتبط.

لقد لعبت للتو مع نمط لتسلسل الوظائف مع عامل التشغيل الحالي |> . على سبيل المثال ، هذه التعريفات:
"" جوليا

منقي

مرشح MyFilter الثابت {F}
flt :: F
النهاية

الوظيفة (mf :: MyFilter) (المصدر)
عامل التصفية (mf.flt ، المصدر)
النهاية

وظيفة Base.filter (FLT)
MyFilter (FLT)
النهاية

يأخذ

ثابت MyTake
ن :: Int64
النهاية

الوظيفة (mt :: MyTake) (المصدر)
تأخذ (المصدر ، طن متري)
النهاية

وظيفة Base.take (ن)
MyTake (اسم)
النهاية

خريطة

ثابت MyMap {F}
و :: F
النهاية

الوظيفة (مم :: MyMap) (المصدر)
الخريطة (mm.f ، المصدر)
النهاية

الوظيفة Base.map (f)
MyMap (و)
النهاية
جوليا enable this to work:
1:10 |> عامل التصفية (i-> i٪ 2 == 0) |> خذ (2) |> الخريطة (i-> i ^ 2) |> جمع
`` Essentially the idea is that functions like filter return a functor if they are called without a source argument, and then these functors all take one argument, namely whatever is "coming" from the left side of the |> . The |> `` ثم اربط كل هذه العناصر معًا.

يمكن أيضًا أن يقوم filter وما إلى ذلك بإرجاع دالة مجهولة تأخذ وسيطة واحدة ، ولست متأكدًا من أي من هذه الخيارات سيكون أكثر فاعلية.

في المثال الخاص بي ، أقوم بالكتابة فوق map(f::Any) في Base ، لا أفهم حقًا ما يفعله التعريف الحالي لـ map ...

لقد توصلت للتو إلى هذا النمط ، ونظراتي السريعة إلى حد ما حولنا لم تظهر أي مناقشة لشيء من هذا القبيل. ماذا يعتقد الناس؟ هل هذا مفيد؟ هل يمكن للناس التفكير في عيوب هذا؟ إذا نجح ذلك ، فربما يكون التصميم الحالي مرنًا بالفعل بما يكفي لتمكين قصة تسلسل شاملة جدًا؟

هذا لا يبدو عمليًا للوظائف التعسفية ، فقط تلك التي تم تعريف MyF من أجلها؟

نعم ، هذا يعمل فقط للوظائف التي يمكن الاشتراك فيها. من الواضح أنه ليس حلاً عامًا جدًا ، وبنفس المعنى نفس القصة كما هو الحال مع Vectorization ، ولكن مع ذلك ، نظرًا لأنه ليس كل ما نأمله سيجعله لـ 1.0 ، فقد يتيح هذا النمط مجموعة كاملة من السيناريوهات حيث يتعين على الأشخاص اللجوء إلى وحدات الماكرو الآن.

الفكرة الأساسية هي أن الدوال مثل المرشح تعيد المنفذ إذا تم استدعاؤها بدون وسيطة المصدر ، ثم تأخذ كل هذه الدوال حجة واحدة ، أي ما هو "قادم" من الجانب الأيسر من |>.

هذا هو ، تقريبًا ، جوهر محولات طاقة Clojure. الفرق الملحوظ هو أن Clojure بنى محولات الطاقة فوق مفهوم المخفضات. باختصار ، كل دالة تعمل في مجموعة يمكن أن تتحلل إلى دالة "تعيين" ووظيفة "تقليل" (حتى لو كانت وظيفة "الاختزال" هي ببساطة concat ). تتمثل ميزة تمثيل وظائف التجميع بهذه الطريقة في أنه يمكنك إعادة ترتيب التنفيذ بحيث يمكن ربط كل "الخرائط" (خاصة بالنسبة للمجموعات الكبيرة). المحولات ، إذن ، هي مجرد استخراج لهذه "التعيينات" التي يتم إرجاعها عند استدعائها بدون مجموعة للعمل عليها.

لا داعي لهذا التعقيد. يمكن للوظائف أن تختار طريقة الكاري مع الإغلاق:

Base.map(f)    = (xs...) -> map(f, xs...)
Base.filter(f) = x -> filter(f, x)
Base.take(n)   = x -> take(x, n)

بالطبع ، هذا ليس شيئًا يجب أن تفعله الحزمة لأنه يغير معنى هذه الطرق لجميع الحزم. والقيام بذلك بشكل مجزأ مثل هذا ليس بديهيًا بشكل رهيب - ما الحجج التي يجب أن تحظى بالأولوية؟

أنا أفضل الحل النحوي لموقع الاتصال كما تمت مناقشته أعلاه ، حيث يتم تخفيض f(a, _, b) إلى x -> f(a, x, b) . إنه أمر صعب ، كما هو موضح في المناقشة الطويلة أعلاه.

لا داعي لهذا التعقيد. يمكن للوظائف الاشتراك في الكاري مع الإغلاق

نعم ، لقد اقترحت أعلاه بالفعل ، لم أكن متأكدًا مما إذا كان هناك اختلاف في الأداء بين هذين الأمرين.

أي الحجج يجب أن تحظى بالأولوية؟

نعم ، ثم لدينا بالفعل أشياء مثل filter و take ، حيث في إحدى الحالات لدينا المجموعة على أنها الأولى والأخرى كالحجة الأخيرة ... أشعر بذلك نوعًا ما على الأقل بالنسبة لعمليات التكرار مثل العمليات ، قد تكون هناك إجابة واضحة على هذا السؤال.

بمجرد أن يصبح _ رمزًا خاصًا متاحًا

نعم ، أوافق تمامًا على وجود حل أكثر عمومية ، وقد يكون حل malmaud .

لا يوجد فرق في الأداء لأن عمليات الإغلاق تقوم في الأساس بإنشاء الكود الذي كتبته يدويًا على أي حال. ولكن نظرًا لأنك تقوم بالكتابة فقط ، يمكنك كتابة دالة للقيام بذلك نيابة عنك ( curry(f, as...) = (bs...) -> f(as..., bs...) ). يهتم بالخريطة والتصفية ؛ كانت هناك أيضًا مقترحات في الماضي لتنفيذ curry الذي يقوم بتنفيذ قيمة خاطفة مثل curry(take, _, 2) .

لقد جئت إلى هنا ، لأنني أتعلم حاليًا ثلاث لغات: D والإكسير والآن جوليا. في D ، هناك بناء جملة استدعاء الوظيفة الموحدة ، كما هو الحال في Rust ، في Elixir لديك مشغل الأنابيب . كلاهما ينفذ بشكل أساسي نوع تسلسل الوظائف المقترح هنا وقد أحببت هذه الميزة حقًا في كلتا اللغتين ، نظرًا لأنه من السهل فهمها ، ويبدو أنها سهلة التنفيذ ، ويمكن أن تجعل التعليمات البرمجية باستخدام التدفقات وأنواع أخرى من خطوط أنابيب البيانات أكثر قابلية للقراءة.

لقد رأيت فقط برنامجًا تعليميًا موجزًا ​​حول بناء جملة جوليا حتى الآن ، لكنني بحثت على الفور في Google عن هذه الميزة ، لأنني كنت آمل أن يكون لدى جوليا أيضًا شيء من هذا القبيل. لذلك أعتقد أن هذا هو +1 لطلب الميزة هذا من جانبي.

مرحبا يا رفاق،

يُرجى السماح لي بإجراء +1 لطلب الميزة هذا. هذا مطلوب بشدة. ضع في اعتبارك بناء جملة Scala التالي.

Array(1,2,3,4,5)
  .map(x => x+1)
  .filter(x => x > 5)
  .reduce(_ + _)

سيكون الأشخاص الذين استخدموا Spark أو أدوات البيانات الضخمة الأخرى المستندة إلى MapReduce على دراية كبيرة بهذا التركيب وسيكونون قد كتبوا وظائف كبيرة ومعقدة بهذه الطريقة. حتى R القديمة نسبيًا تسمح بما يلي.

c(1,2,3,4,5) %>%
  {. + 1} %>%
  {.[which(. > 5)]} %>%
  sum

لاحظ الاستخدام الذكي لمجموعات التعليمات البرمجية كبديل للبرمجة الوظيفية المناسبة - ليس الأجمل ، ولكن القوي. في جوليا ، يمكنني القيام بما يلي.

[1,2,3,4,5] |> 
  _ -> map(__ -> __ + 1, _) |>
  _ -> filter(__ -> __ < 5, _) |>
  _ -> reduce(+, _)

لكن هذا فظيع وقبيح. إذا لم نتمكن من الحصول على كود كائني التوجه على غرار لا سكالا ، فإن مشغلي الأنابيب يصبحون مهمين للغاية. إن أبسط حل هو أن يتم تغذية الأنبوب في _المعامل الأول _ ، ما لم يتم استخدام حرف بدل مثل _ بشكل صريح ، ولكن هذا سيكون منطقيًا فقط إذا تم تغيير map لأخذ بنية البيانات كوسيطة ووظيفة أولى كالثانية.

يجب أن يكون هناك أيضًا بعض المكافئ لـ Scala Array(1,2,3).map(_ + 1) لتجنب الإفراط في _ -> _ + 1 وصيغة مشابهة. تعجبني الفكرة أعلاه حيث تتم ترجمة [1,2,3] |> map(~ + 1, _) إلى map(~ -> ~ + 1, [1,2,3]) . شكرا للبحث.

بالنسبة لهذا الأخير ، لدينا البث باستخدام بناء جملة مضغوط [1, 2, 3] .+ 1 إنه إدمان تمامًا. شيء من هذا القبيل للتخفيض (وربما التصفية) سيكون رائعًا إلى حد الجنون ، لكنه يبدو طلبًا كبيرًا.

إنها نقطة معقولة أن نلاحظ أن كلاً من piping و do يتنافسان على وسيطة الوظيفة الأولى.

سوف أذكر أنه يأتي إلى الخيط الجديد الذي لدينا ،
ليست حزمة واحدة ، ولا اثنتان ، ولكن خمس حزم توفر امتدادات لوظيفة أنابيب SISO الأساسية لجوليا ، نحو الصيغ المقترحة.
انظر القائمة على: https://github.com/JuliaLang/julia/issues/5571#issuecomment -205754539

من المعقول أن نلاحظ أن كلاً من piping و لا يقاتل من أجل وسيطة الوظيفة الأولى.

إذا أردنا الحصول على وظيفة أنابيب إضافية في القاعدة ، لم يكن هذا الموضع محددًا بـ _ إلخ.
ثم أعتقد أنه سيضيف الحجج إلى الموقف النهائي ، وليس الأول.
من شأنه أن يجعل الأمر أشبه بـ "التظاهر بالكاري / التطبيق الجزئي"

من المفترض أن تكون رسالتي أعلاه مثالًا بسيطًا مصممًا لتوضيح مشكلات بناء الجملة المعنية.

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

هذه مواقف أفكر فيها. إن القيام بمثل هذه العمليات دون استخدام استدعاءات الأسلوب أو الأنابيب هو أمر غير بداية بسبب الحجم الهائل.

لا أعرف ما هو أفضل تصميم ، فأنا ببساطة أشجع الجميع على النظر في حالات الاستخدام وبناء جملة أكثر تفصيلاً مما هو موجود حاليًا.

يجب أن يعمل هذا في Juno و Julia 0.6

"{جوليا}
باستخدام LazyCall
lazy_call_module_methods مولد قاعدة
lazy_call_module_methods مرشح التكرار

باستخدام ChainRecursive
بدء_سلسلة ()


```{julia}
[1, 2, 3, 4, 5]
<strong i="14">@unweave</strong> ~it + 1
Base.Generator(it)
<strong i="15">@unweave</strong> ~it < 5
Iterators.filter(it)
reduce(+, it)

لدي سؤال بخصوص بعض القواعد النحوية التي رأيتها في التعليقات على هذه المسألة:
https://stackoverflow.com/questions/44520097/method-chaining-in-julia

somedadaism ، القضايا خاصة بالمشكلات وليس "الإعلان" عن أسئلة مكدس الفائض. أيضًا ، ينشط شعب جوليا في SO و (أكثر من ذلك) على https://discourse.julialang.org/. سأكون مندهشا للغاية إذا لم تحصل على رد على معظم الأسئلة هناك بسرعة كبيرة. ومرحبا بكم في جوليا!

Jeez ، لا يصدق كيف يمكن أن يكون هذا معقدا OO. +1 لبعض البنية اللائقة. بالنسبة لي ، فإن الاستخدام الأساسي للأنابيب هو أيضًا العمل مع البيانات (الإطارات). فكر في dplyr. أنا شخصياً لا أهتم حقًا بالمرور بواسطة الأول / الأخير كإعداد افتراضي ولكن أعتقد أن معظم مطوري الحزم سيحصلون على وظائفهم التي تقبل البيانات كحجة أولى - وماذا عن الحجج الاختيارية؟ 1+ لشيء مثل

1 |> sin |> sum(2, _)

كما ذكرنا سابقًا ، فإن سهولة القراءة والبساطة مهمة للغاية. لا أريد تفويت أسلوب dplyr / tidyverse بأكمله للقيام بالأشياء لتحليل البيانات ...

أود أن أضيف أنني وجدت مفيدة جدًا في تركيب Elixir متعدد الأسطر لمشغل الأنابيب أيضًا.

1
|> sin
|> sum(2)
|> println

ما يعادل println(sum(sin(1),2))

فقط لملاحظة اقتراح في عالم جافا سكريبت . يستخدمون عامل التشغيل ? بدلاً من _ أو ~ والذي لدينا بالفعل معنى ( _ لتجاهل شيء ما و ~ مثل بعض الشيء لا أو شكلي). نظرًا لأننا نستخدم حاليًا ? مثل جافا سكريبت ، فقد نستخدمه للعنصر النائب للكاري أيضًا.

هذا ما يبدو عليه اقتراحهم (إنه في جافا سكريبت ، ولكنه صالح أيضًا في جوليا :)

const addOne = add(1, ?); // apply from the left
addOne(2); // 3

const addTen = add(?, 10); // apply from the right
addTen(2); // 12

// with pipeline
let newScore = player.score
  |> add(7, ?)
  |> clamp(0, 100, ?); // shallow stack, the pipe to `clamp` is the same frame as the pipe to `add`.

const maxGreaterThanZero = Math.max(0, ...);
maxGreaterThanZero(1, 2); // 2
maxGreaterThanZero(-1, -2); // 0

ملخص لأنني بدأت في كتابة واحد لأسباب أخرى.
راجع أيضًا قائمتي السابقة للتعليق على الحزم ذات الصلة .

أي عبث بـ _ لا يكسر an يمكن القيام به في 1.x ، لأن https://github.com/JuliaLang/julia/pull/20328

كل هذا يتلخص في خيارين رئيسيين (بخلاف الوضع الراهن).
يمكن تنفيذ كليهما (لجميع المقاصد والأغراض) باستخدام ماكرو لإعادة كتابة بناء الجملة.

العبث بـ _ لإنشاء وظائف مجهولة

StefanKarpinski ' Terse Lambdas ، أو بناء جملة مشابه حيث يشير وجود _ (في تعبير RHS) إلى أن هذا التعبير بأكمله عبارة عن وظيفة مجهولة.

  • يمكن معالجة هذا

    • الشيء الوحيد الذي لا يمكن فعله هو أن (_) لا يتطابق مع _ . التي هي مجرد دالة للهوية ، لذا لا يهم حقًا

    • يمكن تطبيق هذا في كل مكان ، لذا لن يكون مفيدًا فقط مع |> ، ولكن أيضًا مع كتابة الأشياء بشكل مضغوط مثل map(log(7,_), xs) أو log(7, _).(xs) لأخذ السجل مع الأساس 7 لكل عنصر من xs .

    • أنا شخصياً أفضل هذا ، إذا كنا نفعل أي شيء.

العبث بـ |> لجعله يقوم بالتبديلات

تشمل الخيارات:

  • اجعلها تجعلها تتصرف كما لو كانت كاري

    • في الواقع أعتقد أن هذا كسر ، (على الرغم من أنه قد يكون هناك إصدار غير منقطع يتحقق من جدول الطريقة. أعتقد أن هذا أمر محير بدلاً من ذلك)

  • اجعلها تجعل من _ تصرفًا مميزًا (انظر الخيارات أعلاه ، و / أو طرق مختلفة لتزييفها عن طريق إعادة الكتابة)

    • تتمثل إحدى طرق القيام بذلك في السماح بإنشاء وحدات ماكرو infix ، ثم يمكن للمرء كتابة @|>@ وتحديده بالطريقة التي تريدها في الحزم (تم إغلاق هذا بالفعل بمجرد https://github.com/JuliaLang/julia/ القضايا / 11608)

    • أو منحها تلك الخصائص الخاصة بشكل جوهري

  • لدينا الكثير من تطبيقات الماكرو للقيام بذلك ، كما قلت ، انظر قائمة الحزم ذات الصلة الخاصة بي
  • يقترح بعض الأشخاص أيضًا تغييره لجعله (على عكس جميع المشغلين الآخرين) قادرًا على إحداث تعبير على السطر قبل حدوثه حتى لا ينتهي. لذا يمكنك الكتابة
a
|> f
|>g

بدلاً من التيار:

a |>
f |>
g

(لا يمكن تنفيذ هذا باستخدام ماكرو بدون وضع أقواس على الكتلة. كما أن وضع أقواس على الكتلة يجعلها تعمل على أي حال)

  • أنا شخصياً لا أحب هذه المقترحات لأنها تحقق سحرًا فائقًا |> (عامل غير مرغوب فيه بالفعل).

تحرير : كما يشير StefanKarpinski أدناه ، فإن هذا دائمًا ما يكسر التغيير.
لأن شخصًا ما يمكن أن يعتمد على typeof(|>) <: Function .
وهذه التغييرات ستجعله عنصرًا في تركيب اللغة.

خيار المكافأة: لم يحدث أبدًا: أضف الكاري في كل مكان # 554

لقد فات الأوان في اللغة لإضافة الكاري.
سيكون كسرًا مجنونًا ، مضيفًا أكوامًا ضخمة من الغموض في كل مكان.
فقط كن محيرا جدا

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

إنني أميل تمامًا إلى إهمال |> في 0.7 حتى نتمكن من تقديمه لاحقًا بدلالات أكثر فائدة وربما لا تشبه الوظيفة والتي أظن أنها ضرورية لجعل الأنابيب تعمل بشكل جيد.

إنني أميل تمامًا إلى إهمال |> في 0.7 حتى نتمكن من تقديمه لاحقًا بدلالات أكثر فائدة وربما لا تشبه الوظائف والتي أعتقد أنها ضرورية لجعل الأنابيب تعمل بشكل جيد.

حالة الانهيار فقط حتى في تلك القائمة هي عندما |> يجعل الجهة اليمنى في أدعي أن يكون الضمادة.
إما لإدراج حججه في الموضع الأول ، أو في موضع (/ مواضع) الوسيطة الأخيرة ، أو في موضع ثابت آخر (قد يكون الثاني منطقيًا).
بدون استخدام _ كمُحدد للوسيطة التي سيتم إدراجها فيها.

لم يتم تقديم أي مقترحات أخرى عاجلة يأخذها أي شخص في هذا الموضوع على محمل الجد
سأكون مندهشا إذا كانت هناك تعريفات أخرى معقولة ولكن متقطعة لتلك العملية
لم يقترحها أحد بعد في هذه السنوات الأربع الماضية تقريبًا.

على أي حال فإن إهمالها لن يكون أمرًا فظيعًا.
لا يزال بإمكان الحزم التي تستخدمها الحصول عليها عبر إحدى حزم الماكرو.

فكرة أخرى قد تكون الاحتفاظ بـ |> مع السلوك الحالي وتقديم الوظيفة الجديدة تحت اسم مختلف لا يتطلب استخدام مفتاح shift ، مثل \\ (والذي لا حتى التحليل كعامل في الوقت الحالي). تحدثنا عن هذا على Slack مرة واحدة ، لكنني أعتقد أن التاريخ ربما ضاع في رمال الزمن.

غالبًا ما يتم استخدام الأنابيب بشكل تفاعلي ، وتؤثر سهولة كتابة عامل التشغيل على مدى شعور "الضوء" عند استخدامه. قد يكون الحرف الواحد | رائعًا أيضًا.

قد يكون الحرف الواحد | لطيفًا أيضًا.

نعم بشكل تفاعلي ، ولكن بعد ذلك يكفي أن يكون في ملف .juliarc.jl (الذي كان لدي منذ فترة طويلة ؛-p)

لا يتطلب استخدام مفتاح shift

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

هل هناك أي تقليد لاستخدام |> لهذا الغرض؟ [Mathematica] (http://reference.wolfram.com/language/guide/Syntax.html) بها // لتطبيق وظيفة postfix ، والتي يجب أن تكون سهلة الكتابة في معظم لوحات المفاتيح وقد تكون متاحة ، إذا كانت لم يتم استخدامه بالفعل للتعليقات (كما هو الحال في C ++) أو تقسيم عدد صحيح (كما في Python).

شيء ما بداخله | له اتصال جيد ببرمجة shell ، على الرغم من أنه إذا كان من المتوقع أن يكون | منفردًا بطريقة البت أو. هل تم أخذ || أجل OR المنطقي؟ ماذا عن ||| ؟ إن كتابة حرف يصعب الوصول إليه ثلاث مرات ليس أصعب بكثير من كتابته مرة واحدة.

هل هناك أي تقليد لاستخدام |> لهذا الغرض؟

أعتقد أن تقليد |> مشتق من عائلة لغات ML. عندما يتعلق الأمر بالمشغلين ، فإن القليل من مجتمعات لغات البرمجة قد استكشفت هذه المساحة مثل مجتمع ML / Haskell. مجموعة صغيرة من الأمثلة:

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

x %>% { if(. < 5) { a(.) } else { b(.) } }

والتي ستكون مطولة إلى حد ما في Julia نظرًا لاستخدامها عبارات النهاية. على الرغم من أن المثال أعلاه مجرّد ، إلا أن الكثير من الأشخاص يستخدمون نفس البنية عند كتابة كود معالجة البيانات. مع أي من المقترحات الحالية التي تتم مناقشتها ، هل يمكن تحقيق شيء مشابه لما سبق - ربما من خلال استخدام الأقواس؟

أود أيضًا أن أشجع على استخدام مشغل الأنابيب الذي يعطي بعض المؤشرات المرئية على أن الوسائط يتم توجيهها للأمام ، مثل الرمز > . يوفر هذا تلميحًا مفيدًا للمبتدئين وغير المعتادين على البرمجة الوظيفية.

على الرغم من أن الاستخدامات المقترحة لـ |> لا تتعارض مع الاستخدام النموذجي الحالي من الناحية التركيبية ، إلا أنها _ هي _ غير متوافقة مع |> كونها عامل تشغيل - نظرًا لأن معظمها يتضمن منح |> أكثر بكثير قوة من مجرد وظيفة infix. حتى لو كنا متأكدين من أننا نريد الاحتفاظ بـ x |> f |> g ليعني g(f(x)) ، فإن تركه كعامل تشغيل عادي من المحتمل أن يمنع أي تحسينات أخرى. في حين أن تغيير |> إلى عامل غير عامل يقوم بتطبيق وظيفة postfix قد لا يكسر استخدامه _typical_ لتطبيق الوظيفة المتسلسلة ، إلا أنه لا يزال غير مسموح به لأنه قد يقطع _atypical_ الاستخدام |> - أي شيء الذي يعتمد على كونه عامل تشغيل. لا يزال كسر الاستخدام غير العادي معطلاً وبالتالي فهو غير مسموح به في إصدارات 1.x. إذا أردنا القيام بأي من المقترحات المذكورة أعلاه باستخدام |> بقدر ما أستطيع أن أقول ، فنحن بحاجة إلى إنشاء بناء جملة |> بدلاً من دالة في 1.0.

StefanKarpinski هل إنشاء بناء جملة |> بدلاً من وظيفة حتى على الطاولة في الوقت الحالي؟ هل من الممكن وضعها على الطاولة في الوقت المناسب لوضعها في مكانها لـ 1.0؟

StefanKarpinski هل تصنع |> بناء الجملة بدلاً من وظيفة حتى على الطاولة في الوقت الحالي؟ هل من الممكن وضعها على الطاولة في الوقت المناسب لوضعها في مكانها لـ 1.0؟

يتم استبعاده في 0.7 وإزالته تمامًا من 1.0 على الطاولة.
ثم أعده بعض الوقت خلال 1.x كعنصر بناء جملة.
والذي سيكون في تلك المرحلة تغييرًا غير منقطع.

سيحتاج شخص ما إلى القيام بذلك ، لكنني لا أعتقد أنه تغيير صعب للغاية ، لذا نعم ، إنه مطروح على الطاولة.

ما الذي سيتم إهماله |> ؟ تطبيق في Lazy.jl؟

يمكن إهمال x |> f إلى f(x) .

ماذا عن إهمال l> ولكن في نفس الوقت تقديم مثل ll> له نفس سلوك l> ؟

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

يؤثر هذا على نظام Query والأصدقاء بشكل كبير: لقد أنشأت نظامًا مشابهًا تمامًا لتركيب الأنبوب في R tidyverse. الأمر برمته شامل جدًا: فهو يغطي ملف io لسبعة تنسيقات ملفات مجدولة حاليًا (مع اثنين آخرين قريبين جدًا) ، وجميع عمليات الاستعلام (مثل dplyr) والتخطيط (ليس بعيدًا ، لكني متفائل بأنه يمكننا الحصول على شيء يبدو وكأنه ggplot قريبًا). كل هذا يعتمد على التنفيذ الحالي لـ l> ...

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

الخيار الآخر هو جعل x |> f صيغة بديلة لـ f(x) . سيؤدي ذلك إلى كسر الكود الذي يثقل كاهل |> ولكنه يسمح للرمز الذي يستخدم |> لتسلسل الوظائف لمواصلة العمل مع ترك الأشياء مفتوحة لتحسينات إضافية طالما أنها متوافقة مع ذلك.

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

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

ألا يسمح اقتراحي من الأعلى بذلك؟ أي جعل |> خطأ في بناء الجملة في Julia 1.0 ، وجعل ||> يعادل |> . بالنسبة لـ julia 1.0 ، سيكون هذا مصدر إزعاج بسيط للكود الذي يستخدم حاليًا |> لأنه سيتعين على المرء التبديل إلى ||> . لكنني أشعر أن هذا لن يكون سيئًا للغاية ، بالإضافة إلى أنه يمكن أن يكون مؤتمتًا بالكامل. بعد ذلك ، بمجرد أن يكون لدى شخص ما فكرة جيدة مقابل |> ، يمكن إعادة تقديمها في اللغة. في هذه المرحلة ، سيكون هناك كل من ||> و |> تقريبًا ، وأفترض أن ||> سيتلاشى ببطء في الخلفية إذا بدأ الجميع في تبني |> . وبعد ذلك ، في غضون عامين ، يمكن لـ Julia 2.0 إزالة ||> . في رأيي ، من شأن ذلك أ) ألا يسبب أي مشكلة حقيقية لأي شخص في إطار جوليا 1.0 الزمني ، و ب) اترك جميع الخيارات على الطاولة للحصول على حل جيد بالفعل مقابل |> النهاية

|>(x, f) = f(x)
|>(x, tuple::Tuple) = tuple[1](x, tuple[2:endof(tuple)]...) # tuple
|>(x, f, args...) = f(x, args...) # args

x = 1 |> (+, 1, 1) |> (-, 1) |> (*, 2) |> (/, 2) |> (+, 1) |> (*, 2) # tuple
y = 1 |> (+, 1, 1)... |> (-, 1)... |> (*, 2)... |> (/, 2)... |> (+, 1)... |> (*, 2)... # args

ليس من السهل الكتابة عدة مرات ولكن من اليسار إلى اليمين ولا تستخدم الماكرو.

function fibb_tuple(n)
    if n < 3
        return n
    end
    fibb_tuple(n-3) |> (+, fibb_tuple(n-2), fibb_tuple(n-1))
end

function fibb_args(n)
    if n < 3
        return n
    end
    fibb_args(n-3) |> (+, fibb_args(n-2), fibb_args(n-1))...
end

function fibb(n)
    if n < 3
        return n
    end
    fibb(n-3) + fibb(n-2) + fibb(n-1)
end

n = 25

println("fibb_tuple")
<strong i="8">@time</strong> fibb_tuple(1)
println("fibb_args")
<strong i="9">@time</strong> fibb_args(1)
println("fibb")
<strong i="10">@time</strong> fibb(1)

println("tuple")
<strong i="11">@time</strong> fibb_tuple(n)
println("args")
<strong i="12">@time</strong> fibb_args(n)
println("fibb")
<strong i="13">@time</strong> fibb(n)
fibb_tuple
  0.005693 seconds (2.40 k allocations: 135.065 KiB)
fibb_args
  0.003483 seconds (1.06 k allocations: 60.540 KiB)
fibb
  0.002716 seconds (641 allocations: 36.021 KiB)
tuple
  1.331350 seconds (5.41 M allocations: 151.247 MiB, 20.93% gc time)
args
  0.006768 seconds (5 allocations: 176 bytes)
fibb
  0.006165 seconds (5 allocations: 176 bytes)

|>(x, tuple::Tuple) = tuple[1](x, tuple[2:endof(tuple)]...) أمر مروع.
يحتاج |>(x, f, args...) = f(x, args...) إلى أحرف أكثر ولكنه سريع.

أعتقد أن السماح ببنية مثل subject |> verb(_, objects) مثل verb(subject, objects) يعني دعم SVO (لكن Julia الافتراضي هو VSO أو VOS). ومع ذلك ، تدعم جوليا mutltidipatch بحيث يمكن أن يكون الموضوع موضوعات. أعتقد أننا يجب أن نسمح ببنية مثل (subject1, subject2) |> verb(_, _, object1, object2) مثل verb(subject1, subject2, object1, object2) إذا قدمنا ​​بنية SVO.
إنه MIMO إذا تم إدراكه كخط أنابيب ، كما أشار

ماذا عن استخدام (x)f كـ f(x) ؟
يمكن قراءة كل من (x)f(y) على أنهما f(x)(y) و f(y)(x) لذا اختر التقييم الصحيح أولاً:

(x)f # f(x)
(x)f(y) # f(y)(x)
(x)f(y)(z) # f(y)(z)(x)
(x)(y)f(z) # f(z)(y)(x)
(a)(b)f(c)(d) # f(c)(d)(b)(a)
1(2(3, 4), 5(6, 7), 8(9, 10)) # 1(2(3, 4), 5(6, 7), 8(9, 10))
1 <| (2 <| (3, 4), 5 <| (6, 7), 8 <| (9, 10)) # 1(2(3, 4), 5(6, 7), 8(9, 10)), but 2 <| (3, 4) == 2((3, 4)) so currently emit error
3 |> 2(_, 4) |> 1(_, 5(6, 7), 8(9, 10)) # 1(2(3, 4), 5(6, 7), 8(9, 10))
((3)2(_, 4))1(_, 5(6, 7), 8(9, 10)) # 1(2(3, 4), 5(6, 7), 8(9, 10))
(3, 4) |> 2(_, _) |> 1(_, 5(6, 7), 8(9, 10)) # 1(2(3, 4), 5(6, 7), 8(9, 10))
((3, 4)2)1(_, 5(6, 7), 8(9, 10)) # 1(2(3, 4), 5(6, 7), 8(9, 10))

هذا يمكن أن يعالج vararg بشكل واضح.
لكنها ستكسر العوامل الثنائية بدون مسافات:

(a + b)+(c + d) # +(c + d)(a + b) == (c + d)(a + b): Error

خيار بديل: إضافة حالة أخرى لبناء الجملة. احصل على f ... (x) desugar إلى (args ...) -> f (x، args ...)

هذا من شأنه أن يمكّن من قلي خفيف الوزن (يدويًا) نحويًا:

#Basic example:
f(a,b,c,d) = #some definition
f...(a)(b,c,d) == f(a,b,c,d)
f...(a,b)(c,d) == f(a,b,c,d)
f...(a,b,c)(d) == f(a,b,c,d)
f...(a)...(b)(c,d) == f(a,b,c,d) # etc etc

# Use in pipelining:
x |> map...(f) |> g  |> filter...(h) |> sum

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

جوليا لديها عامل تشغيل بالفعل. ما أقترحه سيكون له نفس السلوك تمامًا مثل عامل splat الحالي.

I، e: f ... (x) == (args ...) -> f (x، args ...) هو سكر لصنع لامدا بالرش.

يمنحك هذا التعريف دائمًا كائنًا وظيفيًا. من المفترض أنك تريد أحيانًا إجابة.

يمكنك الحصول على ذلك من خلال استدعاء الكائن صراحة في النهاية. على سبيل المثال ، لاحظ عدم وجود ... قبل آخر مجموعة من الأقواس في المثال الأخير الخاص بي f ... (أ) ... (ب) (ج ، د) == و (أ ، ب ، ج ، د) .

يمكنك أيضًا استدعاء كائن الوظيفة المرتجعة باستخدام |> ، مما يجعله رائعًا للتوصيل.

تضمين التغريدة

مثال أساسي:

و (أ ، ب ، ج ، د) = # بعض التعريف
و ... (أ) (ب ، ج ، د) == و (أ ، ب ، ج ، د)
و ... (أ ، ب) (ج ، د) == و (أ ، ب ، ج ، د)
و ... (أ ، ب ، ج) (د) == و (أ ، ب ، ج ، د)
و ... (أ) ... (ب) (ج ، د) == و (أ ، ب ، ج ، د) # إلخ إلخ

حدس جيد لاستخدام splat مع تسلسل الوظائف ولكنه معقد للغاية من وجهة نظري.
لقد تقدمت بطلبات متعددة في نقطة واحدة من السلسلة.
في تسلسل الوظائف ، تقوم بإجراء تطبيق خطوة بخطوة على طول السلسلة.

و StefanKarpinski على حق ، فأنت لا تعرف متى تتوقف لتطبيق الوظائف على أنفسهم وتطبيقها في النهاية على عنصر أكثر عددًا.

- (مقطوع) -

آسف ، هذا ما لا معنى له وغير قابل للقراءة.
انظر رسالتي الثانية أدناه للحصول على شرح أوضح (آمل).

بالنظر إلى مدى كفاءة جوليا بالفعل ، فأنا أحب تمامًا فكرة @

يمكنك حتى وضع نموذج أولي لها من الرد الخاص بك:

ctranspose(f) = (a...) -> (b...) -> f(a..., b...)

map'(+)(1:10)

map'(+)'(1:10, 11:20)(21:30)

(+)'(1,2,3)(4,5)

1:10 |> map'(x->x^2) |> filter'(iseven)

لديه نوع من الشعور الجميل تجاهه ، على ما أعتقد.

تحرير: قد يكون هذا الشعور هو السبيل لتعميم هذا أكثر. إذا تمكنا من كتابة map∘(+, 1:10) فيمكننا كتابة map∘(_, 1:10) لوضع الوسيطة الملفوفة أولاً ، ويحدد كاري opertor نطاق lambda ، مما يحل المشكلة الأكبر لمثل هذا الكاري العام.

إيه ، هذا ذكي ،MikeInnes.

أنا أحب كيف تظهر قابلية جوليا الشديدة للتوسع هنا أيضًا. تبين أن الحل الموحد لمجموعة واسعة جدًا من متطلبات تسلسل الوظائف يسيء استخدام ctranspose ...

(توضيح: أحصل على 1:10 |> map'(x->x^2) |> filter'(iseven) مع هذا الاقتراح ، لذا فأنا 💯٪ مقابل ذلك!)

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

ربما يجب علينا تقديم مشغل يونيكود جديد؟ http://www.fileformat.info/info/unicode/char/1f35b/index.htm

(آسف...)

أشعر أن _ لا تزال طريقة أكثر مرونة لصنع لامدا

bramtayl أعتقد أن الفكرة في تعديل MikeInnes لمنشوره هي أن الاثنين يمكن أن يتعايشا - ستعمل الشُرَط السفلية المستقلة كما في @stevengj طلب السحب ، والكاري المستقل كما في فكرة مايك أعلاه سيعمل ، والجمع بين الاثنين سيعمل أيضًا ، مما يتيح لك استخدام عامل الكاري لتحديد نطاق _ s بداخله.

آه حصلت عليه

هذا يجعلها لا تختلف كثيرا عن LazyCall.jl

أو الاقتراح هنا: https://github.com/JuliaLang/julia/pull/24990#issuecomment -350490856

وعلى صعيد أكثر جدية:

لأكون واضحًا ، لا أعتقد أنه يجب علينا بالفعل إساءة استخدام عامل التشغيل adjoint لهذا الغرض

ربما اختيار سليم. ومع ذلك ، أود أن أعبر عن آمالي في أنه إذا تم تنفيذ مثل هذا الحل ، فسيتم منحه عامل تشغيل يسهل كتابته. القدرة على القيام بشيء مثل 1:10 |> map'(x->x^2) أقل فائدة بشكل ملحوظ إذا كان أي حرف يحل محل ' يتطلب مني البحث عنه في جدول unicode (أو استخدام محرر يدعم توسعات LaTeX).

بدلاً من إساءة استخدام عامل التشغيل المساعد ، يمكننا إعادة استخدام عامل التشغيل الإضافي.

  • في سياق الأنابيب (الخطي)
  • في الداخل ، في مكالمة وظيفية

    • قم بإجراء تنبيه قبل وليس بعد

وبالتالي

  • تنبيه يمكن أن يحفز وسيط مكرر مفقود

نوع من تنبيه عالي المستوى ، (مع anacrusis إذا كان هناك بعض الموسيقيين هناك).
على أمل أن لا تهز كثيرا اللغة.

مثال

1:10
    |> map(...x->x^2)
    |> filter(...iseven)

مثال 2

genpie = (r, a=2pi, n=12) ->
  (0:n-1) |>
      map(...i -> a*i/n) |>
      map(...t -> [r*cos(t), r*sin(t)]) 

يمكن أن يقف ل

elmap = f -> (s -> map(f,s))

genpie = (r, a=2pi, n=12) ->
  (0:n-1) |>
      elmap(i -> a*i/n) |>
      elmap(t -> [r*cos(t), r*sin(t)]) 

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

mutable struct T
    move
    scale
    display
    x
    y
end

function move(x,y)
    t.x=x
    t.y=y
    return t
end
function scale(c)
    t.x*=c
    t.y*=c
    return t
end
function display()
    @printf("(%f,%f)\n",t.x,t.y)
end

function newT(x,y)
    T(move,scale,display,x,y)
end


julia> t=newT(0,0)
T(move, scale, display, 0, 0)

julia> t.move(1,2).scale(3).display()
(3.000000,6.000000)

يبدو بناء الجملة مشابهًا جدًا للـ OOP التقليدي ، مع كون "طرق الفصل" قابلة للتغيير. لست متأكدا ما هي الآثار المترتبة على الأداء.

ivanctong ما وصفته هو شيء أقرب إلى واجهة بطلاقة من تسلسل الوظائف.

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

الطريقة التي يعمل بها الإكسير حيث يمر مشغل الأنابيب دائمًا في الجانب الأيسر كحجة أولى ويسمح بحجج إضافية بعد ذلك ، كانت مفيدة جدًا ، أود أن أرى شيئًا مثل "elixir" |> String.ends_with?("ixir") كمواطن من الدرجة الأولى في جوليا.

تعرفه اللغات الأخرى على أنها Uniform Function Call Syntax .
تقدم هذه الميزة العديد من المزايا (انظر ويكيبيديا) ، سيكون من الجيد أن تدعمها جوليا.

فهل هناك واجهة طلاقة لجوليا في هذه المرحلة؟

يرجى إرسال الأسئلة إلى منتدى مناقشة خطاب جوليا .

في نوبة من القرصنة (والحكم المشكوك فيه !؟) قمت بإنشاء حل آخر ممكن لضيق ربط العناصر النائبة للوظائف:

https://github.com/c42f/MagicUnderscores.jl

كما هو مذكور في https://github.com/JuliaLang/julia/pull/24990 ، يعتمد هذا على الملاحظة التي مفادها أن المرء غالبًا ما يريد فتحات معينة من وظيفة معينة لربط تعبير العنصر النائب _ بإحكام ، و الآخرين بشكل فضفاض. MagicUnderscores يجعل هذا قابل للتوسيع لأي وظيفة يحددها المستخدم (في روح آلية البث). وبالتالي يمكننا الحصول على أشياء مثل

julia> <strong i="12">@_</strong> [1,2,3,4] |> filter(_>2, _)
2-element Array{Int64,1}:
 3
 4

julia> <strong i="13">@_</strong> [1,2,3,4] |> filter(_>2, _) |> length
2

"فقط العمل". (مع اختفاء @_ إذا كان من الممكن جعل هذا حلاً عامًا.)

قد تبدو بعض الاختلافات في اقتراح MikeInnes مناسبة لاحتياجاتي (عادةً ما تكون سلاسل طويلة من التصفية ، والخريطة ، والتقليل ، والتعداد ، والرمز البريدي وما إلى ذلك باستخدام بناء الجملة do ).

c(f) = (a...) -> (b...) -> f(a..., b...)

1:10 |> c(map)() do x
    x^2
end |> c(filter)() do x
    x > 50
end

يعمل هذا ، على الرغم من أنني لا أستطيع الحصول على ' للعمل بعد الآن. إنه أقصر قليلاً من:

1:10 |> x -> map(x) do x
    x^2
end |> x -> filter(x) do x
    x > 50
end

كما أعتقد أنه يمكن للمرء أن يفعل

cmap = c(map)
cfilter = c(filter)
cetc = c(etc)
...

1:10 |> cmap() do x
    x^2
end |> cfilter() do x
    x > 50
end |> cetc() do ...

اعتبارًا من 1.0 ، ستحتاج إلى زيادة تحميل adjoint بدلاً من ctranspose . يمكنك أيضًا القيام بما يلي:

julia> Base.getindex(f::Function, x...) = (y...) -> f(x..., y...)

julia> 1:10 |> map[x -> x^2] |> filter[x -> x>50]
3-element Array{Int64,1}:
  64
  81
 100

إذا تمكنا من زيادة التحميل على apply_type فيمكننا الحصول على map{x -> x^2} :)

MikeInnes لقد سرقت ذلك للتو

مساهمة متأخرة وتافهة إلى حد ما - ماذا عن توصيل البيانات إلى أي مكان في قائمة الوسائط باستخدام مجموعة من مشغلي الكاري الأيمن والأيسر:

VERSION==v"0.6.2"
import Base: ctranspose, transpose  
ctranspose(f::Function) = (a...) -> ((b...) -> f(a..., b...))  
 transpose(f::Function) = (a...) -> ((b...) -> f(b..., a...))

"little" |> (*)'''("Mary ")("had ")("a ") |> (*).'(" lamb")

يحتوي Clojure على بعض وحدات ماكرو الخيوط اللطيفة. هل لدينا هؤلاء في نظام جوليا البيئي في مكان ما؟

يحتوي Clojure على بعض وحدات ماكرو الخيوط اللطيفة. هل لدينا هؤلاء في نظام جوليا البيئي في مكان ما؟

https://github.com/MikeInnes/Lazy.jl

يحتوي Clojure على بعض وحدات ماكرو الخيوط اللطيفة. هل لدينا هؤلاء في نظام جوليا البيئي في مكان ما؟

لدينا ما لا يقل عن 10 منهم.
لقد نشرت قائمة أخرى في الموضوع.
https://github.com/JuliaLang/julia/issues/5571#issuecomment -205754539

هل يمكنك تعديل القائمة بحيث تحتوي على LightQuery بدلاً من حزمتين الأخريين؟

نظرًا لأن عامل التشغيل |> يأتي من الإكسير ، فلماذا لا تستلهم من إحدى الطرق التي يجب عليهم بها إنشاء وظائف مجهولة؟
في الإكسير ، يمكنك استخدام &expr لتعريف دالة مجهولة جديدة و &n لالتقاط الوسائط الموضعية ( &1 هي الوسيطات الأولى ، &2 هي الثانية ، إلخ.)
في الإكسير ، هناك أشياء إضافية تكتبها (على سبيل المثال ، تحتاج إلى نقطة قبل الأقواس لاستدعاء دالة مجهولة &(&1 + 1).(10) )

لكن هذا ما يمكن أن تبدو عليه في جوليا

&(&1 * 10)        # same as: v -> v * 10
&(&2 + 2*&5)      # same as: (_, x, _, _, y) -> x + 2*y
&map(sqrt, &1)    # same as: v -> map(sqtr, v)

لذلك يمكننا استخدام عامل التشغيل |> بشكل أفضل

1:9 |> &map(&1) do x
  x^2
end |> &filter(&1) do x
  x in 25:50
end

بدلا من

1:9 |> v -> map(v) do x
  x^2
end |> v -> filter(v) do x
  x in 25:50
end

لاحظ أنه يمكنك استبدال السطر 2 و 3 بـ .|> &(&1^2) أو .|> (v -> v^2)

الاختلاف الرئيسي مع المقترحات التي تحتوي على العنصر النائب _ هو أنه من الممكن هنا استخدام الوسائط الموضعية ، و & أمام التعبيرات تجعل نطاق العناصر النائبة واضحًا (للقارئ و المترجم).

لاحظ أنني أخذت & في الأمثلة ، لكن استخدام ? أو _ أو $ أو أي شيء آخر بدلاً من ذلك ، لن يغير أي شيء قضية.

يستخدم Scala _ للوسيطة الأولى ، و _ للوسيطة الثانية ، وما إلى ذلك ، وهو أمر موجز ، ولكنك تنفد سريعًا من المواقف التي يمكنك من خلالها تطبيقها (لا يمكن التكرار أو العكس ترتيب الحجج). كما أنه لا يحتوي على بادئة ( & في الاقتراح أعلاه) التي تزيل الغموض عن الوظائف من التعبيرات ، وهذه ، في الممارسة العملية ، هي مشكلة أخرى تمنع استخدامها. بصفتك ممارسًا ، ينتهي بك الأمر بلف الوظائف المضمنة المقصودة في أقواس إضافية وأقواس متعرجة ، على أمل أن يتم التعرف عليها.

لذلك أقول إن الأولوية القصوى عند تقديم بناء جملة مثل هذا هو أنه لا لبس فيه.

ولكن بالنسبة لبادئات الوسيطات ، فإن $ له تقليد في عالم البرمجة النصية للقذيفة. من الجيد دائمًا استخدام شخصيات مألوفة. إذا كان |> من Elixir ، فقد يكون ذلك حجة لأخذ & من Elixir أيضًا ، مع فكرة أن المستخدمين يفكرون بالفعل في هذا الوضع. (بافتراض وجود الكثير من مستخدمي الإكسير السابقين ...)

الشيء الوحيد الذي ربما لا يمكن أبدًا في بناء جملة مثل هذا التقاطه هو إنشاء دالة تأخذ وسيطات N ولكنها تستخدم أقل من N. يشير $1 ، $2 ، $3 في الجسم وجود 3 حجج ، ولكن إذا كنت ترغب في وضع هذا في موضع حيث سيتم استدعاؤه مع 4 وسيطات (الأخيرة التي يتم تجاهلها) ، فلا توجد طريقة طبيعية للتعبير عنها. (بخلاف التحديد المسبق لوظائف الهوية لكل N ولف التعبير بواحدة من هؤلاء.) هذا غير مناسب للحالة المحفزة لوضعها بعد |> ، والتي تحتوي على وسيطة واحدة فقط.

لقد وسعت خدعة MikeInnes المتمثلة في زيادة التحميل على getindex ، باستخدام Colon كما لو كانت الدوال عبارة عن مصفوفات:

struct LazyCall{F} <: Function
    func::F
    args::Tuple
    kw::Dict
end

Base.getindex(f::Function,args...;kw...) = LazyCall{typeof(f)}(f,args,kw)

function (lf::LazyCall)(vals...; kwvals...)

    # keywords are free
    kw = merge(lf.kw, kwvals)

    # indices of free variables
    x_ = findall(x->isa(x,Colon),lf.args)
    # indices of fixed variables
    x! = setdiff(1:length(lf.args),x_)

    # the calling order is aligned with the empty spots
    xs = vcat(zip(x_,vals)...,zip(x!,lf.args[x!])...)
    args = map(x->x[2],sort(xs;by=x->x[1]))

    # unused vals go to the end
    callit = lf.func(args...,vals[length(x_)+1:end]...; kw...)

    return callit
end

[1,2,3,4,1,1,5]|> replace![ : , 1=>10, 3=>300, count=2]|> filter[>(50)]  # == [300]

log[2](2) == log[:,2](2) == log[2][2]() == log[2,2]()  # == true

إنه أبطأ بكثير من lambdas أو وحدات ماكرو الخيوط ، لكنني أعتقد أنه رائع للغاية: p

لتذكير الأشخاص الذين يعلقون هنا ، ألق نظرة على المناقشة ذات الصلة على https://github.com/JuliaLang/julia/pull/24990.

أيضًا ، أود أن أشجعك على تجربة https://github.com/c42f/Underscores.jl الذي يوفر تنفيذًا ملائمًا لتسلسل الوظائف _ بناء جملة للعناصر النائبة. jpivarski بناءً على الأمثلة الخاصة بك ، قد تجدها مألوفة ومريحة إلى حد ما.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات