digitalmars.D.learn - Reflections on isPalindrome
- =?UTF-8?B?Ik5vcmRsw7Z3Ig==?= (38/38) Oct 24 2014 I would appreciate comments on my palindrome predicate function
- H. S. Teoh via Digitalmars-d-learn (27/45) Oct 24 2014 [...]
- Peter Alexander (18/25) Oct 24 2014 Aside: for templates, just let the compiler infer @safe and pure.
- =?UTF-8?B?Ik5vcmRsw7Z3Ig==?= (2/30) Oct 24 2014 Clever. Thx!
- =?UTF-8?B?Ik5vcmRsw7Z3Ig==?= (3/5) Oct 26 2014 I extended my algorithm with a minLength argument
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (3/9) Oct 27 2014 You could add an early `return false;` if the range has length
- =?UTF-8?B?Ik5vcmRsw7Z3Ig==?= (3/5) Oct 27 2014 See update :)
- Andrea Fontana (3/8) Oct 27 2014 And you can return true if length <= 1
- =?UTF-8?B?Ik5vcmRsw7Z3Ig==?= (4/6) Oct 27 2014 popBack() only for
- MattCoder (29/29) Oct 28 2014 Hi,
- MattCoder (4/6) Oct 28 2014 I forgot to say that I'm compiling with DMD without any compiler
- =?UTF-8?B?Ik5vcmRsw7Z3Ig==?= (7/9) Oct 28 2014 Try compiling with DMD flag
- MattCoder (6/15) Oct 28 2014 Interesting!
- =?UTF-8?B?Ik5vcmRsw7Z3Ig==?= (3/5) Oct 28 2014 That is great to hear!
- MachineCode (11/32) Oct 28 2014 I'm very surprise. If they either equal or fast sometimes the
- MattCoder (5/16) Oct 29 2014 Yes, I'm curious about this too. I will check the assembly output
- Andrea Fontana (3/5) Oct 28 2014 I mean: you should write a different version for
I would appreciate comments on my palindrome predicate function bool isPalindrome(R)(in R range) safe pure if (isBidirectionalRange!(R)) { import std.range: retro, take; import std.algorithm: equal; static if (hasLength!R) { const mid = range.length/2; // too long for string return equal(range.retro.take(mid), range.take(mid)); } else { return range.retro.equal(range); } } unittest { assert("dallassallad".isPalindrome); assert(!"ab".isPalindrome); assert("a".isPalindrome); assert("åäå".isPalindrome); assert("".isPalindrome); assert([1, 2, 2, 1].isPalindrome); } alias isSymmetrical = isPalindrome; also defined here https://github.com/nordlow/justd/blob/master/algorithm_ex.d#L773 Specifically I wonder what to do with const mid = range.length/2; // TODO too long for string when R is a string. Further, I would like to extend isPalindrome() with a minimum length argument minLength that for string and wstring does import std.uni: byDchar; range.byDchar.array.length >= minLength. AFAIK this will however prevent my algorithm from being single-pass right?
Oct 24 2014
On Fri, Oct 24, 2014 at 09:56:18PM +0000, "Nordlöw" via Digitalmars-d-learn wrote:I would appreciate comments on my palindrome predicate function bool isPalindrome(R)(in R range) safe pure if (isBidirectionalRange!(R)) { import std.range: retro, take; import std.algorithm: equal; static if (hasLength!R) { const mid = range.length/2; // too long for string return equal(range.retro.take(mid), range.take(mid)); } else { return range.retro.equal(range); } }[...] I'd just do it the simple way using range primitives: bool isPalindrome(R)(in R range) if (isBidirectionalRange!R) { auto r = range.save; while (!r.empty) { if (r.front != r.back) return false; r.popFront(); r.popBack(); } return true; } This is guaranteed to work on all bidirectional ranges in single-pass, handles odd/even lengths correctly, and doesn't depend on .length. Offhand remark: in general you shouldn't annotate template functions with safe or pure. Instead, let the compiler infer those attributes, and use safe/pure unittests with known safe/pure ranges to ensure your code itself doesn't introduce any un- safe or impure operations. Otherwise, your function cannot be used with a range that has un- safe or impure range methods. T -- Государство делает вид, что платит нам зарплату, а мы делаем вид, что работаем.
Oct 24 2014
On Friday, 24 October 2014 at 21:56:20 UTC, Nordlöw wrote:bool isPalindrome(R)(in R range) safe pureAside: for templates, just let the compiler infer safe and pure. You don't know whether the range operations on R are pure or not. As for the actual algorithm, there's no need for the random access version, and you bidirectional version does twice as much as necessary: Just do: while (!range.empty) { if (range.front != range.back) return false; range.popFront(); if (range.empty) break; range.popBack(); } return true; This automatically handles narrow strings.Further, I would like to extend isPalindrome() with a minimum length argument minLength that for string and wstring does import std.uni: byDchar; range.byDchar.array.length >= minLength. AFAIK this will however prevent my algorithm from being single-pass right?I'm not sure what you are saying here, but hopefully the above code obviates this anyway.
Oct 24 2014
On Friday, 24 October 2014 at 22:29:12 UTC, Peter Alexander wrote:On Friday, 24 October 2014 at 21:56:20 UTC, Nordlöw wrote:Clever. Thx!bool isPalindrome(R)(in R range) safe pureAside: for templates, just let the compiler infer safe and pure. You don't know whether the range operations on R are pure or not. As for the actual algorithm, there's no need for the random access version, and you bidirectional version does twice as much as necessary: Just do: while (!range.empty) { if (range.front != range.back) return false; range.popFront(); if (range.empty) break; range.popBack(); } return true; This automatically handles narrow strings.Further, I would like to extend isPalindrome() with a minimum length argument minLength that for string and wstring does import std.uni: byDchar; range.byDchar.array.length >= minLength. AFAIK this will however prevent my algorithm from being single-pass right?I'm not sure what you are saying here, but hopefully the above code obviates this anyway.
Oct 24 2014
On Friday, 24 October 2014 at 22:29:12 UTC, Peter Alexander wrote:I extended my algorithm with a minLength argument https://github.com/nordlow/justd/blob/master/algorithm_ex.d#L774Further, I would like to extend isPalindrome() with a minimum length argument minLength that for string and wstring does
Oct 26 2014
On Sunday, 26 October 2014 at 20:38:29 UTC, Nordlöw wrote:On Friday, 24 October 2014 at 22:29:12 UTC, Peter Alexander wrote:You could add an early `return false;` if the range has length and it is less than minLength.I extended my algorithm with a minLength argument https://github.com/nordlow/justd/blob/master/algorithm_ex.d#L774Further, I would like to extend isPalindrome() with a minimum length argument minLength that for string and wstring does
Oct 27 2014
On Monday, 27 October 2014 at 12:10:59 UTC, Marc Schütz wrote:You could add an early `return false;` if the range has length and it is less than minLength.See update :) Thanks!
Oct 27 2014
On Monday, 27 October 2014 at 16:59:19 UTC, Nordlöw wrote:On Monday, 27 October 2014 at 12:10:59 UTC, Marc Schütz wrote:And you can return true if length <= 1 Why bidirectional range only?You could add an early `return false;` if the range has length and it is less than minLength.See update :) Thanks!
Oct 27 2014
On Monday, 27 October 2014 at 21:28:17 UTC, Andrea Fontana wrote:And you can return true if length <= 1Thanks.Why bidirectional range only?popBack() only for http://dlang.org/phobos/std_range.html#isBidirectionalRange
Oct 27 2014
Hi, I don't know if I'm missing something but I did some tests with the popFront and popBack version: bool isPalindrome(R)(R range) if (isBidirectionalRange!(R)) { while (!range.empty){ if (range.front != range.back) return false; range.popFront(); if (range.empty) break; range.popBack(); } return true; } Against the older known version (or implementation): bool isPalindrome2(R)(R r){ auto len = r.length; auto mid = len/2; --len; for(auto i=0;i<mid;++i){ if(r[i]!=r[len-i]){ return false; } } return true; } And in my benchmark test, the first version is 3x "slower" than the second one. Matheus.
Oct 28 2014
On Tuesday, 28 October 2014 at 11:48:37 UTC, MattCoder wrote:And in my benchmark test, the first version is 3x "slower" than the second one.I forgot to say that I'm compiling with DMD without any compiler hints/optimizations. Matheus.
Oct 28 2014
On Tuesday, 28 October 2014 at 11:51:42 UTC, MattCoder wrote:I forgot to say that I'm compiling with DMD without any compiler hints/optimizations.Try compiling with DMD flag -release and perhaps also -release -noboundscheck to get relevant results. DMD is currently not that good at inlining range primitives.
Oct 28 2014
On Tuesday, 28 October 2014 at 13:30:05 UTC, Nordlöw wrote:On Tuesday, 28 October 2014 at 11:51:42 UTC, MattCoder wrote:Interesting! With -release the second version still faster but only by 10%. Now with: -release -noboundscheck they are equal and sometimes your version is slightly faster by ~3 milliseconds. Matheus.I forgot to say that I'm compiling with DMD without any compiler hints/optimizations.Try compiling with DMD flag -release and perhaps also -release -noboundscheck to get relevant results. DMD is currently not that good at inlining range primitives.
Oct 28 2014
On Tuesday, 28 October 2014 at 14:09:50 UTC, MattCoder wrote:Now with: -release -noboundscheck they are equal and sometimes your version is slightly faster by ~3 milliseconds.That is great to hear! You should try profiling with ldc aswell.
Oct 28 2014
On Tuesday, 28 October 2014 at 14:09:50 UTC, MattCoder wrote:On Tuesday, 28 October 2014 at 13:30:05 UTC, Nordlöw wrote:I'm very surprise. If they either equal or fast sometimes the compiler did great optizations or it's just a multicore processor that's helping or what else? the first version (from your post, the one using ranges) change in each iteration two pointers (in pop*() calls) and request r's length 3 times (in .empty calls) while the second doesn't, just run until an already know index (when enter in the loop) and access two index in each iteration. This without consider the amount of ifs. I don't know, maybe I just thinking in the C-way as that code would run.On Tuesday, 28 October 2014 at 11:51:42 UTC, MattCoder wrote:Interesting! With -release the second version still faster but only by 10%. Now with: -release -noboundscheck they are equal and sometimes your version is slightly faster by ~3 milliseconds. Matheus.I forgot to say that I'm compiling with DMD without any compiler hints/optimizations.Try compiling with DMD flag -release and perhaps also -release -noboundscheck to get relevant results. DMD is currently not that good at inlining range primitives.
Oct 28 2014
On Tuesday, 28 October 2014 at 16:07:38 UTC, MachineCode wrote:I'm very surprise. If they either equal or fast sometimes the compiler did great optizations or it's just a multicore processor that's helping or what else? the first version (from your post, the one using ranges) change in each iteration two pointers (in pop*() calls) and request r's length 3 times (in .empty calls) while the second doesn't, just run until an already know index (when enter in the loop) and access two index in each iteration. This without consider the amount of ifs. I don't know, maybe I just thinking in the C-way as that code would run.Yes, I'm curious about this too. I will check the assembly output later (When I have free time) to understand what is happening and why popFront/Back are faster than a loop. Matheus.
Oct 29 2014
On Monday, 27 October 2014 at 22:53:57 UTC, Nordlöw wrote:I mean: you should write a different version for non-bidirectional ranges too :)Why bidirectional range only?popBack() only for
Oct 28 2014