سؤال

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

(defn tally-map
 " Create a map where the keys are all of the unique elements in the input
   sequence and the values represent the number of times those elements
   occur. Note that the keys may not be formatted as conventional Clojure
   keys, i.e. a colon preceding a symbol."
  [aseq]
  (apply merge-with + (map (fn [x] {x 1}) aseq)))

(defn mode
 " Calculate the mode. Rosner p. 13. The mode is problematic in that it may
   not be unique and may not exist at all for a particular group of data.
   If there is a single unique mode, it is returned. If there are multiple
   modes, they are returned as a list. If there is no mode, that is all
   elements are present in equal frequency, nil is returned."
  [aseq]
  (let [amap (tally-map aseq)
        mx (apply max (vals amap))
        k (keys amap)
        f (fn [x] (not (nil? x)))
        modes (filter f (map #(if (= mx (get amap %)) %) k))
        ]
    (cond (= 1 (count modes)) (first modes)
      (every? #(= mx %) (vals amap)) nil
      :else modes)
    )
  )

وهناك زوجين من الأشياء لدي أسئلة حول:

  1. الحجة.وظيفة يقبل تسلسل واحد.هو أكثر الاصطلاحية لقبول عدد متغير من الحجج مثل إضافة وظيفة ؟
  2. رمز رائحة.يبدو أن "السماح" هو قليلا أكثر تعقيدا من ذلك الكثير من متغير المهام.هل فاتني أي واضحة (أو غير واضحة) يستخدم اللغة أو المكتبة التي من شأنها أن تجعل هذه الطريقة أكثر إيجازا?

شكرا مقدما على المساعدة.

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

المحلول

هنا بلدي يأخذ:

  1. هناك العديد من جوهر clojure الوظائف التي تأخذ تسلسل كما الحجج حين أن البعض الآخر أخذ حجج متعددة ، لذلك لا يوجد الاصطلاحية طريقة في رأيي.إذا كان لديك بالفعل البيانات في تسلسل, وأود أن استخدام seq حجة ، لأنه سيوفر لك دعوة إلى تطبيق.

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

وبصرف النظر عن أن أعيد كتابتها وضع وظيفة من هذا القبيل:

(defn mode [aseq]
  (let [amap (tally-map aseq)
        mx (apply max (vals amap))
        modes (map key (filter #(= mx (val %)) amap))
        c (count modes)]
    (cond
      (= c 1) (first modes)
      (= c (count amap)) nil
      :default modes)))

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

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

هنا الدالة التي يعود دائما seq:

(defn modes [aseq]
  (let [amap (tally-map aseq)
        mx (apply max (vals amap))
        modes (map key (filter #(= mx (val %)) amap))]
    (when (< (count modes) (count amap)) modes)))

نصائح أخرى

في رأيي, تعيين بعض الوظائف أكثر من مجموعة ثم على الفور التكثيف القائمة وصولا إلى عنصر واحد هو إشارة إلى استخدام reduce.

(defn tally-map [coll]
  (reduce (fn [h n]
            (assoc h n (inc (h n 0))))
          {} coll))

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

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

أنا أتفق مع الآخرين يجب أن يكون mode إرجاع قائمة النتائج حتى لو كان هناك واحد فقط ؛ انها سوف تجعل حياتك أسهل.ربما تسميته modes بينما كنت في ذلك.

وهنا لطيفة وموجزة تنفيذ mode:

(defn mode [data] 
  (first (last (sort-by second (frequencies data)))))

هذه مآثر الحقائق التالية:

  • على frequencies الدالة ترجع خريطة القيم -> ترددات
  • يمكنك علاج الخريطة كما تسلسل أزواج قيمة المفتاح
  • إذا كنت نوع هذا التسلسل حسب القيمة ( second البند في كل زوج) ، ثم العنصر الأخير في تسلسل تمثل الوضعية ،

تحرير

إذا كنت ترغب في التعامل مع وضع متعددة الحالة ثم يمكنك إدراج إضافية partition-by للحفاظ على جميع القيم مع أقصى قدر من التردد:

(defn modes [data] 
  (->> data
       frequencies 
       (sort-by second)
       (partition-by second)
       last
       (map first)))

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

f (fn [x] (not (nil? x)))
mode (filter f (map #(if (= mx (get amap %)) %) k))

مع

mode (remove nil? (map #(if (= mx (get amap %)) %) k))

(أنا لا أعرف لماذا شيئا مثل not-nil? ليس في clojure.core;انها شيء واحد يحتاج كل يوم.)

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

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

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