Yesterday i was not able to find a proc for the array difference known from other languages like Ruby. I think I was searching for it already five years ago when I was new to Nim.
It is available maybe with a different name already? As we use & for concatenation of sequences, using - from Ruby may be not the best solution, but I have no idea for a better name. Of course writing such a tiny functions yourself takes only a few minutes, but it always interrupts the workflow, and a few months later one is searching for the same function again. Of course, for algorithm which works a lot with differences, using sets as fundamental data structures is generally the better solution. But there can be reasons why a seq or array is used, maybe as it is needed elsewhere, maybe because order is important or maybe because the destination should still contain repeated elements.
Unfortunately Nim lacks still a lot of functions available in other languages, starting with the plain isOdd() and isEven() for numeric variables. Recently I found one needed proc in the foreign itertools module, at the end of a long journey: Google search for proc name or task, finding Python itertools lib, and remembering that I saw a Nim itertools years ago.
Untested:
# https://ruby-doc.org/core-2.6/Array.html#method-i-2D
# Array Difference
import sets
proc `-`*[T](a, b: openArray[T]): seq[T] =
let s = b.toHashSet
result = newSeq[T](a.len)
var i = 0
for el in a:
if el notin s:
result[i] = el
inc(i)
result.setLen(i)
proc `-=`*[T](a: var seq[T]; b: openArray[T]) =
let s = b.toHashSet
var i = 0
var j = 0
while i < a.len:
if a[i] notin s:
a[j] = a[i]
inc(j)
inc(i)
a.setLen(a.len - (i - j))
proc main =
let a = [1, 2, 5, 2, 9, 7, 0]
let b = [7, 4, 1, 10, 7]
echo a - b
echo b - a
var x = @a
x -= b
echo x
main()
~/cdttest $ ./l
@[2, 5, 2, 9, 0]
@[4, 10]
@[2, 5, 2, 9, 0]
- would be a good name candidate
Unfortunately not really. You may have missed the & story many years ago :-)
Araq used & instead of + for seq concatenation. One of his motivation was that he later might use + as "[1, 2] + [3, 4]" resulting in [4, 6]. The other reason may be that array concatenation is not really a math operation. So as we have & now for concatenation, we may want to reserve - for "[1, 2] - [3, 4]" resulting in [-2, -2].
For var seq there's keepIf already (which should have been named retain)
import sequtils, sets, sugar
let a = [1, 2, 5, 2, 9, 7, 0]
let b = [7, 4, 1, 10, 7]
let bSet = b.toHashSet()
echo a.filter((x) => x notin bSet)
Unfortunately Nim lacks still a lot of functions available in other languages, starting with the plain isOdd() and isEven() for numeric variables.
Either my calendar is wrong, or your April's fools joke is several days late.
At least isOdd() and isEven() does not appear when I type that function names into the search box at
https://nim-lang.org/docs/lib.html
So where should they hide?
Well, google still can not find it, and I just tried, it does not work out of the box when I create a new Nim file and type "echo isOdd(1)". So after some thinking of some of your former post, I get the feeling that it is just your attitude: WE DONT NEED IT again!
So let me explain it again, just for you: IsEven() and isOdd() is a more high level construct than "x and 1 == 0" or "x mod 2 == 0". And isEven and isOddn is easier to read and to type. While above numeric expressions may by easy and are simple to grap, even and odd needs absolutely no thinking, which I generally prefer. And the math expression is sensible to typing errors.
So whenever I need even/odd decision, I create an isEven/isOdd proc. I did it a dozen times in Nim. Luckily other languages like Ruby or even the systems programming language Rust provides these functions. Ruby just calls it even/odd.
I forgot the implementation detail: Most people may use "i mod 2 == 0". Are we sure that this collapse to a simple bit test in assembly when i is signed? Well I think yes for gcc, but can not really remember, so I generally fall back to the even more low level concept "i and 1 == 0"
as far as I know, python does not have a is_even / is_odd in stdlib (you can find packages). Not an expert of Rust, but I could not find it in stdlib through google (I did find crates with that).
Relevant (serious) link: https://rosettacode.org/wiki/Even_or_odd
Relevant (funny) link: https://twitter.com/ctrlshifti/status/1288745146759000064
It seems the way to go would be to create packages for it. There is actually space for two naturally competing packages about it: iseven and isodd...
Ruby has even() and odd() for sure, I have used Ruby for some years. And I saw isOdd() isEven() in Rust API docs, but I am not sure if that was a extern crate (package).
Of course it is an old joke that that some people created packages which contained only the odd()/even() functions. But that is absolutely no reason to refuse to adding these functions to Nims std lib. As the procs are tiny and with "is" prefix there is no risk of name space pollution.
So whenever I need even/odd decision, I create an isEven/isOdd proc. I did it a dozen times in Nim.
I don't mind adding isOdd/isEven to math.nim but I do wonder why you did it a dozen times already. What kind of programs do you write? I don't understand. Most programs don't need isEven just like they don't need isPrime or isDivisibleBy, regardless how you spell it.
Most of these modules contain somewhere a even/odd decision, and whoever has used Ruby, misses these functions.
That's what I wonder about. I can see odd/even useful for a chessboard and for highlighting every odd line in a table. And that's it, 2 use cases are known to me. :-)
Back to the initial topic:
let bSet = b.toHashSet()
echo a.filter((x) => x notin bSet)
is a smart solution,and all your mentioned points are true. But in Ruby we can just write seq1 - seq2, and I strongly assume this works also in other high level languages like Julia, Python, Crystal. While a seq difference does rarely occur in real life, people coming from other languages may ask: How can I write it in Nim? And when we point beginners to you two lines code we simple loose many beginners in early state. And I have to admit that your solution is not a no-brainer for me. Beginners may even try to solve the task theirself and come to O(n^2) solutions.
Python doesn't support - on "lists" and Python survives. It's fundamentally an operation on sets and not on seqs anyway.
How can I write it in Nim? And when we point beginners to you two lines code we simple loose many beginners in early state.
And otherwise we lose the same people two days later when something else comes up that is not available as a builtin solution. It's a silly way of designing things and teaching people that everything should be a one-liner is counter productive. Beginners should learn programming, not how to get away without thinking too hard about anything.
That's what I wonder about. I can see odd/even useful for a chessboard and for highlighting every odd line in a table. And that's it, 2 use cases are known to me. :-)
For me it occurs nearly in each module: When working with trees, like the RTree or a MinMaxHeap actions depend on the fact that current depth of level is odd or even. For chess we need it really often, not only for board position and active player color, but also for depth in search tree, odd/even determines if player is white/black. In geometric data processing odd/even is an important query, when we determine the intersection of shapes, as line intersection with polygon in a simple case. Or for graphics, odd/even winding number determines filled or unfilled areas.
But I understand, for compiler and web development even/odd is rarely used :-)
The highlighting example should be implemented via an iterator().advance_by(2), no check is needed. So only one use-case left :P
Rust doesn't have those functions, even though their std is opulent. Someone made the crate for this, don't know, ironically or not. Hope it's not buggy.
To play devil's advocate: it's natural to expect idOdd() from a language which recommends using inc() instead of += 1. ;)
And when we point beginners to you two lines code we simple loose many beginners in early state.
I beg to differ. When a beginner asks a question I think it's very beneficial to show him both a naïve/classic approach and the more idiomatic and recommended one. It should only attract those beginners any language community really wants - interested and motivated. My original answer to this topic was formulated exactly how I loved getting answers as a complete beginner (I still do) - give the alternative solution and explain how it's better.
Thank you for complimenting my solution, but I don't really think it's smart. I think it's proper and it's a bit sad it still looks foreign to many.
To play devil's advocate: it's natural to expect isOdd() from a language which recommends using inc() instead of += 1
As I said, I don't mind isOdd/isEven, I also like setBit instead of |=.
Is the real problem about discoverability in the documentation?
Difference of two sets is already there, though not with seq.
Do we need to improve the documentation search engine ([CTRL]+[F])? Do we need better or more descriptive proc names? Should we search also in the description of the procs or by categories?
You may have some trouble following this long discussion :-)
Of course set difference is available, but we may need it for arrays and sequences. The plain solution converting the seqs to set, do difference operation and convert back is not always what we want, as we may want to preserve duplicates and order. As I was aware of the naming problem, I had some hope that the desired proc may exist with different name already. Unfortunately miram led the discussion in a very different and wired direction and confused me and maybe many others. The solution from zoom is not bad, I hope I will remember it.
You may have some trouble following this long discussion :-)
There's no need for this.