Five functions to get the digits of a number












0















I'm writing a simple function: given a number, it will return a sequence or a collection of the digits in the number (in the correct order). i.e (get-digits-fn 1234567) => (1 2 3 4 5 6 7) / [1 2 3 4 5 6 7]



Below are five attempts at the same function:



(defn get-digits-1 [num]
(->> [num '()]
(iterate (fn [[num digits]]
(when (> num 0)
[(quot num 10) (conj digits (rem num 10))])))
(take-while some?)
(last)
(second)))

(defn get-digits-2 [num]
(when (> num 0)
(lazy-seq (concat (get-digits-2 (quot num 10)) '((rem num 10))))))

;; Suggested by Carcigenate
(defn get-digits-3 [num]
(->> (str)
(map str)
(map int)
(into '())))

(defn get-digits-4 [num]
(loop [n num
res '()]
(if (= n 0)
res
(recur (quot n 10) (conj res (rem n 10))))))

(defn get-digits-5 [num]
(->>
(iterate (fn [[n digits]]
[(quot n 10) (conj digits (rem n 10))])
[num '()])
(drop-while #(not= 0 (first %)))
(first)
(second)))


A helper function for testing performance:



(defn quick-bench-get-digits [fn range]
(quick-bench (->> range
(map fn)
(map (partial apply vector))
(into ))))


The perf results (output truncated to only show execution time mean):



eul> (quick-bench-get-digits get-digits-1 (range 1 100000))
Execution time mean : 129.516521 ms

eul> (quick-bench-get-digits get-digits-2 (range 1 100000))
Execution time mean : 128.637055 ms

eul> (quick-bench-get-digits get-digits-3 (range 1 100000))
Execution time mean : 24.267716 ms

eul> (quick-bench-get-digits get-digits-4 (range 1 100000))
Execution time mean : 25.083393 ms

eul> (quick-bench-get-digits get-digits-5 (range 1 100000))
Execution time mean : 145.430443 ms


It looks like get-digits-3 is the fastest while get-digits-4 is closely behind. (As the numbers increase, get-digits-3 outperforms get-digits-4. i.e try (range 1000000 2000000))




  1. Any way to increase performance more without leaving Clojure land?

  2. If mutability and Java inter-op is allowed, is there a way to increase performance?


p.s. functions 1 and 5 are almost identical. This was incremental exploration.










share|improve this question

























  • Is performance of this code specifically really a concern? I'm doubtful that this is what would be the choking point of the program. 24ms is very fast, especially for an inflated test case.

    – Carcigenicate
    Nov 9 '18 at 17:59


















0















I'm writing a simple function: given a number, it will return a sequence or a collection of the digits in the number (in the correct order). i.e (get-digits-fn 1234567) => (1 2 3 4 5 6 7) / [1 2 3 4 5 6 7]



Below are five attempts at the same function:



(defn get-digits-1 [num]
(->> [num '()]
(iterate (fn [[num digits]]
(when (> num 0)
[(quot num 10) (conj digits (rem num 10))])))
(take-while some?)
(last)
(second)))

(defn get-digits-2 [num]
(when (> num 0)
(lazy-seq (concat (get-digits-2 (quot num 10)) '((rem num 10))))))

;; Suggested by Carcigenate
(defn get-digits-3 [num]
(->> (str)
(map str)
(map int)
(into '())))

(defn get-digits-4 [num]
(loop [n num
res '()]
(if (= n 0)
res
(recur (quot n 10) (conj res (rem n 10))))))

(defn get-digits-5 [num]
(->>
(iterate (fn [[n digits]]
[(quot n 10) (conj digits (rem n 10))])
[num '()])
(drop-while #(not= 0 (first %)))
(first)
(second)))


A helper function for testing performance:



(defn quick-bench-get-digits [fn range]
(quick-bench (->> range
(map fn)
(map (partial apply vector))
(into ))))


The perf results (output truncated to only show execution time mean):



eul> (quick-bench-get-digits get-digits-1 (range 1 100000))
Execution time mean : 129.516521 ms

eul> (quick-bench-get-digits get-digits-2 (range 1 100000))
Execution time mean : 128.637055 ms

eul> (quick-bench-get-digits get-digits-3 (range 1 100000))
Execution time mean : 24.267716 ms

eul> (quick-bench-get-digits get-digits-4 (range 1 100000))
Execution time mean : 25.083393 ms

eul> (quick-bench-get-digits get-digits-5 (range 1 100000))
Execution time mean : 145.430443 ms


It looks like get-digits-3 is the fastest while get-digits-4 is closely behind. (As the numbers increase, get-digits-3 outperforms get-digits-4. i.e try (range 1000000 2000000))




  1. Any way to increase performance more without leaving Clojure land?

  2. If mutability and Java inter-op is allowed, is there a way to increase performance?


p.s. functions 1 and 5 are almost identical. This was incremental exploration.










share|improve this question

























  • Is performance of this code specifically really a concern? I'm doubtful that this is what would be the choking point of the program. 24ms is very fast, especially for an inflated test case.

    – Carcigenicate
    Nov 9 '18 at 17:59
















0












0








0








I'm writing a simple function: given a number, it will return a sequence or a collection of the digits in the number (in the correct order). i.e (get-digits-fn 1234567) => (1 2 3 4 5 6 7) / [1 2 3 4 5 6 7]



Below are five attempts at the same function:



(defn get-digits-1 [num]
(->> [num '()]
(iterate (fn [[num digits]]
(when (> num 0)
[(quot num 10) (conj digits (rem num 10))])))
(take-while some?)
(last)
(second)))

(defn get-digits-2 [num]
(when (> num 0)
(lazy-seq (concat (get-digits-2 (quot num 10)) '((rem num 10))))))

;; Suggested by Carcigenate
(defn get-digits-3 [num]
(->> (str)
(map str)
(map int)
(into '())))

(defn get-digits-4 [num]
(loop [n num
res '()]
(if (= n 0)
res
(recur (quot n 10) (conj res (rem n 10))))))

(defn get-digits-5 [num]
(->>
(iterate (fn [[n digits]]
[(quot n 10) (conj digits (rem n 10))])
[num '()])
(drop-while #(not= 0 (first %)))
(first)
(second)))


A helper function for testing performance:



(defn quick-bench-get-digits [fn range]
(quick-bench (->> range
(map fn)
(map (partial apply vector))
(into ))))


The perf results (output truncated to only show execution time mean):



eul> (quick-bench-get-digits get-digits-1 (range 1 100000))
Execution time mean : 129.516521 ms

eul> (quick-bench-get-digits get-digits-2 (range 1 100000))
Execution time mean : 128.637055 ms

eul> (quick-bench-get-digits get-digits-3 (range 1 100000))
Execution time mean : 24.267716 ms

eul> (quick-bench-get-digits get-digits-4 (range 1 100000))
Execution time mean : 25.083393 ms

eul> (quick-bench-get-digits get-digits-5 (range 1 100000))
Execution time mean : 145.430443 ms


It looks like get-digits-3 is the fastest while get-digits-4 is closely behind. (As the numbers increase, get-digits-3 outperforms get-digits-4. i.e try (range 1000000 2000000))




  1. Any way to increase performance more without leaving Clojure land?

  2. If mutability and Java inter-op is allowed, is there a way to increase performance?


p.s. functions 1 and 5 are almost identical. This was incremental exploration.










share|improve this question
















I'm writing a simple function: given a number, it will return a sequence or a collection of the digits in the number (in the correct order). i.e (get-digits-fn 1234567) => (1 2 3 4 5 6 7) / [1 2 3 4 5 6 7]



Below are five attempts at the same function:



(defn get-digits-1 [num]
(->> [num '()]
(iterate (fn [[num digits]]
(when (> num 0)
[(quot num 10) (conj digits (rem num 10))])))
(take-while some?)
(last)
(second)))

(defn get-digits-2 [num]
(when (> num 0)
(lazy-seq (concat (get-digits-2 (quot num 10)) '((rem num 10))))))

;; Suggested by Carcigenate
(defn get-digits-3 [num]
(->> (str)
(map str)
(map int)
(into '())))

(defn get-digits-4 [num]
(loop [n num
res '()]
(if (= n 0)
res
(recur (quot n 10) (conj res (rem n 10))))))

(defn get-digits-5 [num]
(->>
(iterate (fn [[n digits]]
[(quot n 10) (conj digits (rem n 10))])
[num '()])
(drop-while #(not= 0 (first %)))
(first)
(second)))


A helper function for testing performance:



(defn quick-bench-get-digits [fn range]
(quick-bench (->> range
(map fn)
(map (partial apply vector))
(into ))))


The perf results (output truncated to only show execution time mean):



eul> (quick-bench-get-digits get-digits-1 (range 1 100000))
Execution time mean : 129.516521 ms

eul> (quick-bench-get-digits get-digits-2 (range 1 100000))
Execution time mean : 128.637055 ms

eul> (quick-bench-get-digits get-digits-3 (range 1 100000))
Execution time mean : 24.267716 ms

eul> (quick-bench-get-digits get-digits-4 (range 1 100000))
Execution time mean : 25.083393 ms

eul> (quick-bench-get-digits get-digits-5 (range 1 100000))
Execution time mean : 145.430443 ms


It looks like get-digits-3 is the fastest while get-digits-4 is closely behind. (As the numbers increase, get-digits-3 outperforms get-digits-4. i.e try (range 1000000 2000000))




  1. Any way to increase performance more without leaving Clojure land?

  2. If mutability and Java inter-op is allowed, is there a way to increase performance?


p.s. functions 1 and 5 are almost identical. This was incremental exploration.







performance comparative-review clojure






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 9 '18 at 4:39









200_success

129k15152414




129k15152414










asked Nov 9 '18 at 3:47









nakiyanakiya

1165




1165













  • Is performance of this code specifically really a concern? I'm doubtful that this is what would be the choking point of the program. 24ms is very fast, especially for an inflated test case.

    – Carcigenicate
    Nov 9 '18 at 17:59





















  • Is performance of this code specifically really a concern? I'm doubtful that this is what would be the choking point of the program. 24ms is very fast, especially for an inflated test case.

    – Carcigenicate
    Nov 9 '18 at 17:59



















Is performance of this code specifically really a concern? I'm doubtful that this is what would be the choking point of the program. 24ms is very fast, especially for an inflated test case.

– Carcigenicate
Nov 9 '18 at 17:59







Is performance of this code specifically really a concern? I'm doubtful that this is what would be the choking point of the program. 24ms is very fast, especially for an inflated test case.

– Carcigenicate
Nov 9 '18 at 17:59












1 Answer
1






active

oldest

votes


















1














I'm afraid your code has errors, so your benchmarking is invalid.




  • The get-digits-3 function is wrong.

  • A corrected version runs about twelve times slower than
    get-digits-4.


Repairing get-digits-3



Your get-digits-3 doesn't work:



=> (get-digits-3 123456789)
()


The function chain should be applied to the argument num:



(defn get-digits-3 [num]
(->> num
(str)
(map str)
(map int)
(into '())))


But this fails:



=> (get-digits-3 123456789)
ClassCastException java.base/java.lang.String cannot be cast to java.base/java.lang.Character clojure.lang.RT.intCast (RT.java:1213)


The culprit is the (map int) line. You can't apply int to strings. If you drop the offending line, you get the character values:



=> (get-digits-3 123456789)
(57 56 55 54 53 52 51 50 49)


Notice, too, that they are in reverse order. So we




  • subtract the value of character 0 and


  • conj onto the other end by using a vector.


This gives us ...



(defn get-digits-3 [num]
(->> num
(str)
(map int)
(map #(- % (int )))
(into )))

=> (get-digits-3 123456789)
[1 2 3 4 5 6 7 8 9]


Benchmarking



I went about this differently:




  • I used a single example instead of a range.

  • I used Criterium's bench rather than its quick-bench.


The results were as follow:



For your original (faulty) version of get-digits-3:



=> (bench (get-digits-3 123456789))
...
Execution time mean : 228.998292 ns


For the corrected version:



=> (bench (get-digits-3 123456789))
...
Execution time mean : 3.440024 µs


And for get-digits-4:



=>(bench (get-digits-4 123456789))
...
Execution time mean : 247.502418 ns


This confirms that your faulty get-digits-3 is about as fast as get-digits-4. And get-digits-3 ignores its argument, so is bound to get relatively faster as the numbers get bigger. You can see that the corrected version is nowhere near competitive.






share|improve this answer


























  • 3 µs / 250ns is about twelve times slower, not a hundred times. I think you lost an order of magnitude somewhere.

    – Toby Speight
    yesterday











  • @TobySpeight Thanks. Corrected

    – Thumbnail
    6 hours ago











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%2f207277%2ffive-functions-to-get-the-digits-of-a-number%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














I'm afraid your code has errors, so your benchmarking is invalid.




  • The get-digits-3 function is wrong.

  • A corrected version runs about twelve times slower than
    get-digits-4.


Repairing get-digits-3



Your get-digits-3 doesn't work:



=> (get-digits-3 123456789)
()


The function chain should be applied to the argument num:



(defn get-digits-3 [num]
(->> num
(str)
(map str)
(map int)
(into '())))


But this fails:



=> (get-digits-3 123456789)
ClassCastException java.base/java.lang.String cannot be cast to java.base/java.lang.Character clojure.lang.RT.intCast (RT.java:1213)


The culprit is the (map int) line. You can't apply int to strings. If you drop the offending line, you get the character values:



=> (get-digits-3 123456789)
(57 56 55 54 53 52 51 50 49)


Notice, too, that they are in reverse order. So we




  • subtract the value of character 0 and


  • conj onto the other end by using a vector.


This gives us ...



(defn get-digits-3 [num]
(->> num
(str)
(map int)
(map #(- % (int )))
(into )))

=> (get-digits-3 123456789)
[1 2 3 4 5 6 7 8 9]


Benchmarking



I went about this differently:




  • I used a single example instead of a range.

  • I used Criterium's bench rather than its quick-bench.


The results were as follow:



For your original (faulty) version of get-digits-3:



=> (bench (get-digits-3 123456789))
...
Execution time mean : 228.998292 ns


For the corrected version:



=> (bench (get-digits-3 123456789))
...
Execution time mean : 3.440024 µs


And for get-digits-4:



=>(bench (get-digits-4 123456789))
...
Execution time mean : 247.502418 ns


This confirms that your faulty get-digits-3 is about as fast as get-digits-4. And get-digits-3 ignores its argument, so is bound to get relatively faster as the numbers get bigger. You can see that the corrected version is nowhere near competitive.






share|improve this answer


























  • 3 µs / 250ns is about twelve times slower, not a hundred times. I think you lost an order of magnitude somewhere.

    – Toby Speight
    yesterday











  • @TobySpeight Thanks. Corrected

    – Thumbnail
    6 hours ago
















1














I'm afraid your code has errors, so your benchmarking is invalid.




  • The get-digits-3 function is wrong.

  • A corrected version runs about twelve times slower than
    get-digits-4.


Repairing get-digits-3



Your get-digits-3 doesn't work:



=> (get-digits-3 123456789)
()


The function chain should be applied to the argument num:



(defn get-digits-3 [num]
(->> num
(str)
(map str)
(map int)
(into '())))


But this fails:



=> (get-digits-3 123456789)
ClassCastException java.base/java.lang.String cannot be cast to java.base/java.lang.Character clojure.lang.RT.intCast (RT.java:1213)


The culprit is the (map int) line. You can't apply int to strings. If you drop the offending line, you get the character values:



=> (get-digits-3 123456789)
(57 56 55 54 53 52 51 50 49)


Notice, too, that they are in reverse order. So we




  • subtract the value of character 0 and


  • conj onto the other end by using a vector.


This gives us ...



(defn get-digits-3 [num]
(->> num
(str)
(map int)
(map #(- % (int )))
(into )))

=> (get-digits-3 123456789)
[1 2 3 4 5 6 7 8 9]


Benchmarking



I went about this differently:




  • I used a single example instead of a range.

  • I used Criterium's bench rather than its quick-bench.


The results were as follow:



For your original (faulty) version of get-digits-3:



=> (bench (get-digits-3 123456789))
...
Execution time mean : 228.998292 ns


For the corrected version:



=> (bench (get-digits-3 123456789))
...
Execution time mean : 3.440024 µs


And for get-digits-4:



=>(bench (get-digits-4 123456789))
...
Execution time mean : 247.502418 ns


This confirms that your faulty get-digits-3 is about as fast as get-digits-4. And get-digits-3 ignores its argument, so is bound to get relatively faster as the numbers get bigger. You can see that the corrected version is nowhere near competitive.






share|improve this answer


























  • 3 µs / 250ns is about twelve times slower, not a hundred times. I think you lost an order of magnitude somewhere.

    – Toby Speight
    yesterday











  • @TobySpeight Thanks. Corrected

    – Thumbnail
    6 hours ago














1












1








1







I'm afraid your code has errors, so your benchmarking is invalid.




  • The get-digits-3 function is wrong.

  • A corrected version runs about twelve times slower than
    get-digits-4.


Repairing get-digits-3



Your get-digits-3 doesn't work:



=> (get-digits-3 123456789)
()


The function chain should be applied to the argument num:



(defn get-digits-3 [num]
(->> num
(str)
(map str)
(map int)
(into '())))


But this fails:



=> (get-digits-3 123456789)
ClassCastException java.base/java.lang.String cannot be cast to java.base/java.lang.Character clojure.lang.RT.intCast (RT.java:1213)


The culprit is the (map int) line. You can't apply int to strings. If you drop the offending line, you get the character values:



=> (get-digits-3 123456789)
(57 56 55 54 53 52 51 50 49)


Notice, too, that they are in reverse order. So we




  • subtract the value of character 0 and


  • conj onto the other end by using a vector.


This gives us ...



(defn get-digits-3 [num]
(->> num
(str)
(map int)
(map #(- % (int )))
(into )))

=> (get-digits-3 123456789)
[1 2 3 4 5 6 7 8 9]


Benchmarking



I went about this differently:




  • I used a single example instead of a range.

  • I used Criterium's bench rather than its quick-bench.


The results were as follow:



For your original (faulty) version of get-digits-3:



=> (bench (get-digits-3 123456789))
...
Execution time mean : 228.998292 ns


For the corrected version:



=> (bench (get-digits-3 123456789))
...
Execution time mean : 3.440024 µs


And for get-digits-4:



=>(bench (get-digits-4 123456789))
...
Execution time mean : 247.502418 ns


This confirms that your faulty get-digits-3 is about as fast as get-digits-4. And get-digits-3 ignores its argument, so is bound to get relatively faster as the numbers get bigger. You can see that the corrected version is nowhere near competitive.






share|improve this answer















I'm afraid your code has errors, so your benchmarking is invalid.




  • The get-digits-3 function is wrong.

  • A corrected version runs about twelve times slower than
    get-digits-4.


Repairing get-digits-3



Your get-digits-3 doesn't work:



=> (get-digits-3 123456789)
()


The function chain should be applied to the argument num:



(defn get-digits-3 [num]
(->> num
(str)
(map str)
(map int)
(into '())))


But this fails:



=> (get-digits-3 123456789)
ClassCastException java.base/java.lang.String cannot be cast to java.base/java.lang.Character clojure.lang.RT.intCast (RT.java:1213)


The culprit is the (map int) line. You can't apply int to strings. If you drop the offending line, you get the character values:



=> (get-digits-3 123456789)
(57 56 55 54 53 52 51 50 49)


Notice, too, that they are in reverse order. So we




  • subtract the value of character 0 and


  • conj onto the other end by using a vector.


This gives us ...



(defn get-digits-3 [num]
(->> num
(str)
(map int)
(map #(- % (int )))
(into )))

=> (get-digits-3 123456789)
[1 2 3 4 5 6 7 8 9]


Benchmarking



I went about this differently:




  • I used a single example instead of a range.

  • I used Criterium's bench rather than its quick-bench.


The results were as follow:



For your original (faulty) version of get-digits-3:



=> (bench (get-digits-3 123456789))
...
Execution time mean : 228.998292 ns


For the corrected version:



=> (bench (get-digits-3 123456789))
...
Execution time mean : 3.440024 µs


And for get-digits-4:



=>(bench (get-digits-4 123456789))
...
Execution time mean : 247.502418 ns


This confirms that your faulty get-digits-3 is about as fast as get-digits-4. And get-digits-3 ignores its argument, so is bound to get relatively faster as the numbers get bigger. You can see that the corrected version is nowhere near competitive.







share|improve this answer














share|improve this answer



share|improve this answer








edited 6 hours ago

























answered Dec 13 '18 at 21:48









ThumbnailThumbnail

1,27657




1,27657













  • 3 µs / 250ns is about twelve times slower, not a hundred times. I think you lost an order of magnitude somewhere.

    – Toby Speight
    yesterday











  • @TobySpeight Thanks. Corrected

    – Thumbnail
    6 hours ago



















  • 3 µs / 250ns is about twelve times slower, not a hundred times. I think you lost an order of magnitude somewhere.

    – Toby Speight
    yesterday











  • @TobySpeight Thanks. Corrected

    – Thumbnail
    6 hours ago

















3 µs / 250ns is about twelve times slower, not a hundred times. I think you lost an order of magnitude somewhere.

– Toby Speight
yesterday





3 µs / 250ns is about twelve times slower, not a hundred times. I think you lost an order of magnitude somewhere.

– Toby Speight
yesterday













@TobySpeight Thanks. Corrected

– Thumbnail
6 hours ago





@TobySpeight Thanks. Corrected

– Thumbnail
6 hours ago


















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%2f207277%2ffive-functions-to-get-the-digits-of-a-number%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 reconfigure Docker Trusted Registry 2.x.x to use CEPH FS mount instead of NFS and other traditional...

is 'sed' thread safe

How to make a Squid Proxy server?