Hello,
I have been coding a lot in Nim for the past week or so.
I find the seq type very useful as they work like dynamic arrays i.e. I don't need to plan the seq length in advance. But I've never once needed to use arrays or the open arrays (which can be used just in proc parameters).
So the question is, why would one use arrays over seqs? Is it used more in low level programming to optimize the speed (unlike how I use Nim as a scripting language)?
And then when is the more limited (as in places where it can be used) open arrays used over seq?
Thanks.
Is it used more in low level programming to optimize the speed (unlike how I use Nim as a scripting language)?
Yeah, exactly.
I have been coding a lot in Nim for the past week or so.
It is really interesting that we have a few people who really use Nim as their first serious programming language :-) Unfortunately we have still no real book "Introduction to computer science with Nim".
OpenArrays are just a proc parameter type that accept arrays and seqs. Whenever a proc can be used with array or seq parameters, openArray parameter type should be used. The word OpenArray is indeed a bit strange, it was used in the Wirthian languages Modula and Oberon, we have no better term currently.
Seqs are generally fine when performance and code size is not critical. As seq data structure contains a pointer to the data elements, we have some indirection, which decreases performance.
When number of elements is known at compile time, arrays can be used and offer maximum performance. Nim Arrays are value objects, living on the stack if used inside of procs, so very direct element access is possible. Basically element access is only calculation of an offset, which is the product of element size and index value. If index is constant, than this offset is known at compile time, so access is as fast as access of a plain proc variable. An example is a chess program, we have the 64 fields, so we generally would use a array of 64 elements, not a seq. (Well, beginners may use a two dimensional array of 8 x 8, but then we have two indices, which again increases access time. A plain array is OK for this game, as we can increase index by one when we move a piece to the left, and increase index by 8 when we move forward.)
One more question about syntax:
What reasons in additional type "openarray"? Why compiler can't get "array" as a parameter of procedure?
As I see (from views of novice in Nim, but have a lot of experiense with other langs):
Here's what I might type:
proc count1 (a:openArray[int]): int = # compiler allow to pass array with different length
for i in 0..a.len: result += a[i]
Thank you everyone for your feedback!
FYI, I have updated my notes on arrays, seqs, open arrays here based on my understanding from this thread + manual + a lot of examples.
openarray is used for two things:
Heh, I derived to that same conclusion too.. see the 2nd example in https://scripter.co/notes/nim/#open-arrays :)
for i in 0..a.len: result += a[i]
I made that mistake too when working on that example .. s/len/high/.
import sequtils,macros
const
x = [1]
y = [2,3]
z = [4,5,6]
template arrayConcat[T](arrs: varargs[seq[T],`@`]): auto =
const tmp = unpackVarargs(concat,arrs)
var res: array[tmp.len,T]
for i, x in tmp:
res[i] = x
tmp
const foo = arrayConcat(x,y,z)
assert foo == [1,2,3,4,5,6]
It also works if you add this to the end of @shirleyquirk's program:
assert @x & @y & @z == [1,2,3,4,5,6]
As well as if you put @ before [1,2,3,4,5,6] either above or for foo from arrayConcat.
An "inverse @" (toArray, say) would probably let you get by with one set of CT manipulators in terms of type/signature matching. Run-time is very different, of course. const s barely have addresses, heap|elsewise.
seq[T] will not share buffer like slices in golang - it has its own copy of content. For example
import std/strformat
let a = [1, 2, 3, 4]
let b = a[0..1]
let c = a
let d = b
echo &"typeof(a) = {$typeof(a)}"
echo &"typeof(b) = {$typeof(b)}"
echo &"typeof(c) = {$typeof(c)}"
echo &"typeof(d) = {$typeof(d)}"
echo &"addr a = {cast[uint](addr a):x}"
echo &"addr b = {cast[uint](addr b):x}"
echo &"addr c = {cast[uint](addr c):x}"
echo &"addr d = {cast[uint](addr d):x}"
echo &"addr b[0] = {cast[uint](addr b[0]):x}"
echo &"addr d[0] = {cast[uint](addr d[0]):x}"
Output
typeof(a) = array[0..3, int]
typeof(b) = seq[int]
typeof(c) = array[0..3, int]
typeof(d) = seq[int]
addr a = 555e1b1cd020
addr b = 555e1b1cd200
addr c = 555e1b1cd2c0
addr d = 555e1b1cd210
addr b[0] = 7f521ba0a058
addr d[0] = 7f521ba0a078
So you can't modify a through b, also modify d won't affect a and b.
You don't have to use $ when formatting strings.
echo &"typeof(a) = {typeof(a)}"
And you might be interested in sugar.dump:
import std/sugar
dump typeof(a)
You can also do
echo &"{typeof(a) = }"