Predicate testing for equality that returns the common element












1












$begingroup$


I'm going to try brute-forcing the solving of a Magic Square. To simplify the code later on, I wanted a function similar to =, but one that returns the common element if all the elements are equal and nil otherwise. This is unlike = which always returns either true or false. I'm calling it =? for lack of a better name.



Example:



(=? 1 2 1)
nil ; nil because they're not all the same

(=? 1 1 1)
1 ; Returns the common element of 1


Obviously, this function won't work well when nil may be the sole element in a collection, since it will return falsey even if all the elements are the same, but nil.



I thought that there must be some kind of core operation I could use here, but after a few minutes of thinking, I couldn't think of any. I ended up rolling my own function using loop. I'd like general advice on the function that I wrote, or potentially a more idiomatic way to write it that I'm missing. It looks exceedingly clunky to me; especially after I realized the need for the last-arg accumulator. A recursive solution would be nice, as I fear I'm missing something obvious here:



(defn =?
"Checks if every supplied argument =s every other.
Returns the common element if they're all the same, nil otherwise."
[& args]
(loop [[f-arg & r-args] (rest args)
last-arg (first args)]
(cond
(nil? f-arg) last-arg
(= f-arg last-arg) (recur r-args f-arg)
:else nil)))









share|improve this question











$endgroup$

















    1












    $begingroup$


    I'm going to try brute-forcing the solving of a Magic Square. To simplify the code later on, I wanted a function similar to =, but one that returns the common element if all the elements are equal and nil otherwise. This is unlike = which always returns either true or false. I'm calling it =? for lack of a better name.



    Example:



    (=? 1 2 1)
    nil ; nil because they're not all the same

    (=? 1 1 1)
    1 ; Returns the common element of 1


    Obviously, this function won't work well when nil may be the sole element in a collection, since it will return falsey even if all the elements are the same, but nil.



    I thought that there must be some kind of core operation I could use here, but after a few minutes of thinking, I couldn't think of any. I ended up rolling my own function using loop. I'd like general advice on the function that I wrote, or potentially a more idiomatic way to write it that I'm missing. It looks exceedingly clunky to me; especially after I realized the need for the last-arg accumulator. A recursive solution would be nice, as I fear I'm missing something obvious here:



    (defn =?
    "Checks if every supplied argument =s every other.
    Returns the common element if they're all the same, nil otherwise."
    [& args]
    (loop [[f-arg & r-args] (rest args)
    last-arg (first args)]
    (cond
    (nil? f-arg) last-arg
    (= f-arg last-arg) (recur r-args f-arg)
    :else nil)))









    share|improve this question











    $endgroup$















      1












      1








      1





      $begingroup$


      I'm going to try brute-forcing the solving of a Magic Square. To simplify the code later on, I wanted a function similar to =, but one that returns the common element if all the elements are equal and nil otherwise. This is unlike = which always returns either true or false. I'm calling it =? for lack of a better name.



      Example:



      (=? 1 2 1)
      nil ; nil because they're not all the same

      (=? 1 1 1)
      1 ; Returns the common element of 1


      Obviously, this function won't work well when nil may be the sole element in a collection, since it will return falsey even if all the elements are the same, but nil.



      I thought that there must be some kind of core operation I could use here, but after a few minutes of thinking, I couldn't think of any. I ended up rolling my own function using loop. I'd like general advice on the function that I wrote, or potentially a more idiomatic way to write it that I'm missing. It looks exceedingly clunky to me; especially after I realized the need for the last-arg accumulator. A recursive solution would be nice, as I fear I'm missing something obvious here:



      (defn =?
      "Checks if every supplied argument =s every other.
      Returns the common element if they're all the same, nil otherwise."
      [& args]
      (loop [[f-arg & r-args] (rest args)
      last-arg (first args)]
      (cond
      (nil? f-arg) last-arg
      (= f-arg last-arg) (recur r-args f-arg)
      :else nil)))









      share|improve this question











      $endgroup$




      I'm going to try brute-forcing the solving of a Magic Square. To simplify the code later on, I wanted a function similar to =, but one that returns the common element if all the elements are equal and nil otherwise. This is unlike = which always returns either true or false. I'm calling it =? for lack of a better name.



      Example:



      (=? 1 2 1)
      nil ; nil because they're not all the same

      (=? 1 1 1)
      1 ; Returns the common element of 1


      Obviously, this function won't work well when nil may be the sole element in a collection, since it will return falsey even if all the elements are the same, but nil.



      I thought that there must be some kind of core operation I could use here, but after a few minutes of thinking, I couldn't think of any. I ended up rolling my own function using loop. I'd like general advice on the function that I wrote, or potentially a more idiomatic way to write it that I'm missing. It looks exceedingly clunky to me; especially after I realized the need for the last-arg accumulator. A recursive solution would be nice, as I fear I'm missing something obvious here:



      (defn =?
      "Checks if every supplied argument =s every other.
      Returns the common element if they're all the same, nil otherwise."
      [& args]
      (loop [[f-arg & r-args] (rest args)
      last-arg (first args)]
      (cond
      (nil? f-arg) last-arg
      (= f-arg last-arg) (recur r-args f-arg)
      :else nil)))






      clojure






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Feb 9 '18 at 22:46







      Carcigenicate

















      asked Feb 9 '18 at 21:49









      CarcigenicateCarcigenicate

      3,29211431




      3,29211431






















          2 Answers
          2






          active

          oldest

          votes


















          1












          $begingroup$

          Soon after I posted this, I went for a walk and realized that this is the perfect opportunity to use reduce. I'm kind of embarrassed that I didn't see it originally, but this was my train of thought that lead me to what I'm much happier with.



          First, I tried a simple reduction. This was pretty simple, although still a little verbose:



          (defn =?2 [& args]
          (reduce (fn [acc n]
          (if (= n acc)
          n
          (reduced nil)))
          (first args)
          (rest args)))


          I decided that this was probably simple enough to use a function macro for. Normally I don't like using them for reductions, but there's not much going on here:



          (defn =?3 [& args]
          (reduce #(if (= % %2) % (reduced nil))
          (first args)
          (rest args)))


          I don't like using explicit calls to first and rest, as I find they're usually neater when they're implicit in a deconstruction. I decided to deconstruct the arguments instead:



          (defn =?4 [& [arg & rest-args]]
          (reduce #(if (= % %2) % (reduced nil))
          arg
          rest-args))


          Then I checked what = uses, and found that it just uses multiple argument lists, so I tried that. This strikes me as the more idiomatic solution:



          (defn =?5
          ( nil)
          ([arg] arg)
          ([arg & args]
          (reduce #(if (= % %2) % (reduced nil))
          arg
          args)))


          I'm torn between 4 and 5, but I think that both are a significant improvement.






          share|improve this answer











          $endgroup$





















            0












            $begingroup$

            You don't need the single argument case. You can abbreviate to



            (defn =?5
            ( nil)
            ([arg & args]
            (reduce #(if (= % %2) % (reduced nil))
            arg
            args)))


            However, the following is simpler still:



            (defn =?
            ( nil)
            ([x & xs]
            (when (every? #(= x %) xs) x)))


            We can even fold in the zero-argument case in by destructuring, as you do in =?4:



            (defn =? [& [x & xs]]
            (when (every? #(= x %) xs) x))


            But this is too obscure for me.






            share|improve this answer











            $endgroup$













              Your Answer





              StackExchange.ifUsing("editor", function () {
              return StackExchange.using("mathjaxEditing", function () {
              StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
              StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
              });
              });
              }, "mathjax-editing");

              StackExchange.ifUsing("editor", function () {
              StackExchange.using("externalEditor", function () {
              StackExchange.using("snippets", function () {
              StackExchange.snippets.init();
              });
              });
              }, "code-snippets");

              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "196"
              };
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function() {
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled) {
              StackExchange.using("snippets", function() {
              createEditor();
              });
              }
              else {
              createEditor();
              }
              });

              function createEditor() {
              StackExchange.prepareEditor({
              heartbeatType: 'answer',
              autoActivateHeartbeat: false,
              convertImagesToLinks: false,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: null,
              bindNavPrevention: true,
              postfix: "",
              imageUploader: {
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              },
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              });


              }
              });














              draft saved

              draft discarded


















              StackExchange.ready(
              function () {
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f187221%2fpredicate-testing-for-equality-that-returns-the-common-element%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              1












              $begingroup$

              Soon after I posted this, I went for a walk and realized that this is the perfect opportunity to use reduce. I'm kind of embarrassed that I didn't see it originally, but this was my train of thought that lead me to what I'm much happier with.



              First, I tried a simple reduction. This was pretty simple, although still a little verbose:



              (defn =?2 [& args]
              (reduce (fn [acc n]
              (if (= n acc)
              n
              (reduced nil)))
              (first args)
              (rest args)))


              I decided that this was probably simple enough to use a function macro for. Normally I don't like using them for reductions, but there's not much going on here:



              (defn =?3 [& args]
              (reduce #(if (= % %2) % (reduced nil))
              (first args)
              (rest args)))


              I don't like using explicit calls to first and rest, as I find they're usually neater when they're implicit in a deconstruction. I decided to deconstruct the arguments instead:



              (defn =?4 [& [arg & rest-args]]
              (reduce #(if (= % %2) % (reduced nil))
              arg
              rest-args))


              Then I checked what = uses, and found that it just uses multiple argument lists, so I tried that. This strikes me as the more idiomatic solution:



              (defn =?5
              ( nil)
              ([arg] arg)
              ([arg & args]
              (reduce #(if (= % %2) % (reduced nil))
              arg
              args)))


              I'm torn between 4 and 5, but I think that both are a significant improvement.






              share|improve this answer











              $endgroup$


















                1












                $begingroup$

                Soon after I posted this, I went for a walk and realized that this is the perfect opportunity to use reduce. I'm kind of embarrassed that I didn't see it originally, but this was my train of thought that lead me to what I'm much happier with.



                First, I tried a simple reduction. This was pretty simple, although still a little verbose:



                (defn =?2 [& args]
                (reduce (fn [acc n]
                (if (= n acc)
                n
                (reduced nil)))
                (first args)
                (rest args)))


                I decided that this was probably simple enough to use a function macro for. Normally I don't like using them for reductions, but there's not much going on here:



                (defn =?3 [& args]
                (reduce #(if (= % %2) % (reduced nil))
                (first args)
                (rest args)))


                I don't like using explicit calls to first and rest, as I find they're usually neater when they're implicit in a deconstruction. I decided to deconstruct the arguments instead:



                (defn =?4 [& [arg & rest-args]]
                (reduce #(if (= % %2) % (reduced nil))
                arg
                rest-args))


                Then I checked what = uses, and found that it just uses multiple argument lists, so I tried that. This strikes me as the more idiomatic solution:



                (defn =?5
                ( nil)
                ([arg] arg)
                ([arg & args]
                (reduce #(if (= % %2) % (reduced nil))
                arg
                args)))


                I'm torn between 4 and 5, but I think that both are a significant improvement.






                share|improve this answer











                $endgroup$
















                  1












                  1








                  1





                  $begingroup$

                  Soon after I posted this, I went for a walk and realized that this is the perfect opportunity to use reduce. I'm kind of embarrassed that I didn't see it originally, but this was my train of thought that lead me to what I'm much happier with.



                  First, I tried a simple reduction. This was pretty simple, although still a little verbose:



                  (defn =?2 [& args]
                  (reduce (fn [acc n]
                  (if (= n acc)
                  n
                  (reduced nil)))
                  (first args)
                  (rest args)))


                  I decided that this was probably simple enough to use a function macro for. Normally I don't like using them for reductions, but there's not much going on here:



                  (defn =?3 [& args]
                  (reduce #(if (= % %2) % (reduced nil))
                  (first args)
                  (rest args)))


                  I don't like using explicit calls to first and rest, as I find they're usually neater when they're implicit in a deconstruction. I decided to deconstruct the arguments instead:



                  (defn =?4 [& [arg & rest-args]]
                  (reduce #(if (= % %2) % (reduced nil))
                  arg
                  rest-args))


                  Then I checked what = uses, and found that it just uses multiple argument lists, so I tried that. This strikes me as the more idiomatic solution:



                  (defn =?5
                  ( nil)
                  ([arg] arg)
                  ([arg & args]
                  (reduce #(if (= % %2) % (reduced nil))
                  arg
                  args)))


                  I'm torn between 4 and 5, but I think that both are a significant improvement.






                  share|improve this answer











                  $endgroup$



                  Soon after I posted this, I went for a walk and realized that this is the perfect opportunity to use reduce. I'm kind of embarrassed that I didn't see it originally, but this was my train of thought that lead me to what I'm much happier with.



                  First, I tried a simple reduction. This was pretty simple, although still a little verbose:



                  (defn =?2 [& args]
                  (reduce (fn [acc n]
                  (if (= n acc)
                  n
                  (reduced nil)))
                  (first args)
                  (rest args)))


                  I decided that this was probably simple enough to use a function macro for. Normally I don't like using them for reductions, but there's not much going on here:



                  (defn =?3 [& args]
                  (reduce #(if (= % %2) % (reduced nil))
                  (first args)
                  (rest args)))


                  I don't like using explicit calls to first and rest, as I find they're usually neater when they're implicit in a deconstruction. I decided to deconstruct the arguments instead:



                  (defn =?4 [& [arg & rest-args]]
                  (reduce #(if (= % %2) % (reduced nil))
                  arg
                  rest-args))


                  Then I checked what = uses, and found that it just uses multiple argument lists, so I tried that. This strikes me as the more idiomatic solution:



                  (defn =?5
                  ( nil)
                  ([arg] arg)
                  ([arg & args]
                  (reduce #(if (= % %2) % (reduced nil))
                  arg
                  args)))


                  I'm torn between 4 and 5, but I think that both are a significant improvement.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Feb 9 '18 at 23:18

























                  answered Feb 9 '18 at 23:12









                  CarcigenicateCarcigenicate

                  3,29211431




                  3,29211431

























                      0












                      $begingroup$

                      You don't need the single argument case. You can abbreviate to



                      (defn =?5
                      ( nil)
                      ([arg & args]
                      (reduce #(if (= % %2) % (reduced nil))
                      arg
                      args)))


                      However, the following is simpler still:



                      (defn =?
                      ( nil)
                      ([x & xs]
                      (when (every? #(= x %) xs) x)))


                      We can even fold in the zero-argument case in by destructuring, as you do in =?4:



                      (defn =? [& [x & xs]]
                      (when (every? #(= x %) xs) x))


                      But this is too obscure for me.






                      share|improve this answer











                      $endgroup$


















                        0












                        $begingroup$

                        You don't need the single argument case. You can abbreviate to



                        (defn =?5
                        ( nil)
                        ([arg & args]
                        (reduce #(if (= % %2) % (reduced nil))
                        arg
                        args)))


                        However, the following is simpler still:



                        (defn =?
                        ( nil)
                        ([x & xs]
                        (when (every? #(= x %) xs) x)))


                        We can even fold in the zero-argument case in by destructuring, as you do in =?4:



                        (defn =? [& [x & xs]]
                        (when (every? #(= x %) xs) x))


                        But this is too obscure for me.






                        share|improve this answer











                        $endgroup$
















                          0












                          0








                          0





                          $begingroup$

                          You don't need the single argument case. You can abbreviate to



                          (defn =?5
                          ( nil)
                          ([arg & args]
                          (reduce #(if (= % %2) % (reduced nil))
                          arg
                          args)))


                          However, the following is simpler still:



                          (defn =?
                          ( nil)
                          ([x & xs]
                          (when (every? #(= x %) xs) x)))


                          We can even fold in the zero-argument case in by destructuring, as you do in =?4:



                          (defn =? [& [x & xs]]
                          (when (every? #(= x %) xs) x))


                          But this is too obscure for me.






                          share|improve this answer











                          $endgroup$



                          You don't need the single argument case. You can abbreviate to



                          (defn =?5
                          ( nil)
                          ([arg & args]
                          (reduce #(if (= % %2) % (reduced nil))
                          arg
                          args)))


                          However, the following is simpler still:



                          (defn =?
                          ( nil)
                          ([x & xs]
                          (when (every? #(= x %) xs) x)))


                          We can even fold in the zero-argument case in by destructuring, as you do in =?4:



                          (defn =? [& [x & xs]]
                          (when (every? #(= x %) xs) x))


                          But this is too obscure for me.







                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          edited 53 mins ago

























                          answered 1 hour ago









                          ThumbnailThumbnail

                          1,366510




                          1,366510






























                              draft saved

                              draft discarded




















































                              Thanks for contributing an answer to Code Review Stack Exchange!


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid



                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.


                              Use MathJax to format equations. MathJax reference.


                              To learn more, see our tips on writing great answers.




                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f187221%2fpredicate-testing-for-equality-that-returns-the-common-element%23new-answer', 'question_page');
                              }
                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              Popular posts from this blog

                              How to make a Squid Proxy server?

                              第一次世界大戦

                              Touch on Surface Book