كيف يمكنني مزج وسيطات الكلمات الرئيسية الاختيارية مع الأشياء والراحة؟

StackOverflow https://stackoverflow.com/questions/2753937

  •  02-10-2019
  •  | 
  •  

سؤال

لدي ماكرو يأخذ الجسم:

(defmacro blah [& body] (dostuffwithbody))

لكني أرغب في إضافة وسيطة اختيارية للكلمة الرئيسية أيضًا ، لذلك يمكن أن تبدو أيًا من هذين:

(blah :specialthingy 0 body morebody lotsofbody)
(blah body morebody lotsofboy)

كيف أقوم بذلك؟ لاحظ أنني أستخدم Clojure 1.2 ، لذلك أنا أيضًا أستخدم الأشياء الاختيارية الجديدة لتدمير الوسيطة. حاولت بسذاجة القيام بذلك:

(defmacro blah [& {specialthingy :specialthingy} & body])

لكن من الواضح أن هذا لم ينجح بشكل جيد. كيف يمكنني إنجاز هذا أو شيء مشابه؟

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

المحلول

شيء مثل ما يلي ربما (انظر أيضًا كيف defn وبعض وحدات الماكرو الأخرى في clojure.core محددة):

(defmacro blah [& maybe-option-and-body]
  (let [has-option (= :specialthingy (first maybe-option-and-body))
        option-val (if has-option (second maybe-option-and-body)) ; nil otherwise
        body (if has-option (nnext maybe-option-and-body) maybe-option-and-body)]
    ...))

بدلاً من ذلك ، يمكن أن تكون أكثر تطوراً ؛ قد يكون من المفيد إذا كنت تعتقد أنك قد ترغب في الحصول على خيارات متعددة ممكنة في مرحلة ما:

(defn foo [& args]
  (let [aps (partition-all 2 args)
        [opts-and-vals ps] (split-with #(keyword? (first %)) aps)
        options (into {} (map vec opts-and-vals))
        positionals (reduce into [] ps)]
    [options positionals]))

(foo :a 1 :b 2 3 4 5)
; => [{:a 1, :b 2} [3 4 5]]

نصائح أخرى

أجاب Michał Marczyk على سؤالك بشكل جيد ، ولكن إذا كنت على استعداد لقبول (Foo {} بقية Args) ، وبالتالي فإن الخريطة ذات الكلمات الرئيسية الاختيارية هي الوسيطة الأولى (فارغة في هذا المثال) ، فإن هذا الرمز البسيط سيعمل بخير:

(defn foo [{:keys [a b]} & rest] (list a b rest))
(foo {} 2 3)
=> (nil nil (2 3))
(foo {:a 1} 2 3)
=> (1 nil (2 3))

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

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

(defn foo [& rest] 
  (letfn [(inner-foo [{:keys [a b]} & rest] (list a b rest))] 
    (if (map? (first rest)) 
      (apply inner-foo rest)
      (apply inner-foo {} rest))))

(foo 1 2 3)
=> (nil nil (1 2 3))
(foo {:a 2 :b 3} 4 5 6)
=> (2 3 (4 5 6))
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top