type Pair = tuple
one,two:int
proc modify(p:var Pair,x:int) =
#some steps changing p using x
var list:seq[Pair]
#statements initilizing and modifying list
#The tuples can be modified in forward order - i.e.
for pair in mitems(list):
pair.one += 1
#But modifying the tuples in reverse order would require something like:
var acc:int # or something forcing the reverse order of operation
for pair in mitems(list).reversed:
pair.modify(acc)
acc += pair.one
The actual problem is more complex and a more complex tuple is used instead of Pair . I had hoped to avoid indexing list and repeating the code for modify using lots of indexed tuple members.
An alternate form of for statement that allows pair to be a reference variable would also work - but I do NOT want to allocate the Pair-s on the heap.
type Pair = tuple
one,two:int
proc modify(p:var Pair,x:int) =
p.one = x
p.two = x + 1
var list:seq[Pair] = @[ (0,0), (1,2), (56, 2)]
for ind in countdown(high(list),low(list):
modify(list[ind],ind)
I don't know if this is good enough.
in reverse order?
I think reversed() returns a reversed copy of the sequence, which we may want to avoid.
So we may need a modified form of the mitems iterator:
https://github.com/nim-lang/Nim/blob/master/lib/system.nim
iterator mitems*[T](a: var openArray[T]): var T {.inline.} =
## iterates over each item of `a` so that you can modify the yielded value.
var i = 0
while i < len(a):
yield a[i]
inc(i)
Inverting the loop direction should be not difficult. I have no good idea for the name, maybe it already exists?
Generally, no programming language will have direct library support for all possible ways to iterate over a given data structure. I'm not sure if a reverse mitems iterator should be part of the standard library; however, writing it yourself doesn't incur any overhead that a stdlib implementation could avoid (we're not talking about an interpreted language, where you look for a C implementation in order to gain performance).
And it's pretty easy:
iterator mitems_reversed*[T](a: var openarray[T]): var T =
var i = high(a)
while i >= 0:
yield a[i]
dec i
You can also use a template to alias an lvalue if you want to work with indices:
for i in countdown(high(list), 0):
template pair: auto = list[i]
pair.modify(acc)
acc += pair.one
Keep in mind, though, that complex expressions in the template may be calculated repeatedly, but a counter isn't a problem.
@Jehan will the compiler optimize the access to the array or would it be better to use something like @araq recently showed on how to avoid multiple execution for the index calculation (and lookup).
for i in countdown(high(list), 0):
var pair = addr(list[i])
pair[].modify(acc)
acc += pair[].one
Interesting question -- it was my feeling that we should avoid usage of unsafe addr operator whenever possible, but of course it ensures in this example maximum efficiency.
I think the [] in your example are not really necessary in Nim?
But currently I am confused after reading about addr in manual. First
var i: int j: ptr int = addr i #k: ref int = addr i echo repr(addr(i))
So addr gives a (untraced) ptr as said in manual (commented line does not compile as expected). But output is "ref 0x427c48 --> 0". Same in manual example. But should that be ptr instead of ref in output?
And in manual we have: "Taking the address of an object that resides on the stack is unsafe, as the pointer may live longer than the object on the stack and can thus reference a non-existing object." Sure -- but is usage of addr not unsafe generally. For example "o = newSomething; p = addr(o[])" So p is an untraced pointer, which may refer to the object when it is already garbage collected? Or does Nim's Garbage collector takes that ptr into account and delays garbage collection while ptr exists?
Yeah... that is why I said it looks like voodoo at first ... where @araq did not agree with :)
See: http://forum.nim-lang.org/t/1928
It is ptr and I believe that repr() simply does not care and calls it "reference".
AFAIK. The GC never collects/deletes anything that is on the stack. That is part of its efficiency. Taking the address is only unsafe in regard of "returning". I am not sure about that with iterators and yield though but I think that will be safe too as you can't not have variables simply vanish after the yield statement.
Your example about the "o" and the "p" keeps the "o" alive regardless of "p" so the "p" is safe as long as "o" is alive. If "o" gets deleted "p" points into the void.
I believe its about this being not guaranteed to be safe:
proc main() =
var p: ptr int = nil
block:
var a = 2
p = addr(a)
var a = 4
echo a # different a now, former is not in scope
echo p[] # unsafe may crash
main()
If I am wrong with something please correct me :)
And in manual we have: "Taking the address of an object that resides on the stack is unsafe, as the pointer may live longer than the object on the stack and can thus reference a non-existing object." Sure -- but is usage of addr not unsafe generally.
Ah, ok, I will change the text in the manual. You're absolutely right, 'addr' is unsafe in that both the stack and the GC do not consider these pointers.
OderWat: @Jehan will the compiler optimize the access to the array or would it be better to use something like @araq recently showed on how to avoid multiple execution for the index calculation (and lookup).
A reasonably modern C compiler used as a backend will optimize this case, yes.
That said, something that I'm still struggling with is why people are so reluctant to write their own abstractions (a custom iterator solves the problem just fine) and just insist on either finding stuff in the standard library of a language or inline a complex calculation. (This is not a Nim-specific issue, I see it in other compiled languages all the time, too.)
Nim is not an interpreted language. Writing a procedure/iterator yourself is not going to create more overhead than a stdlib implementation. All you're getting out of this exercise is more convoluted code. Writing reusable abstractions and using them is Software Engineering 101 and should be a non-brainer, really. It's okay to check if there isn't an implementation already that you may have overlooked, but if there isn't one: just write it yourself rather than jumping through hoops trying to avoid that.
I included the alternative because I generally try to avoid telling people how to write their code, but with my Software Engineering hat on, my considered opinion is to just do it cleanly in the first place.
I have multiple impressions about that:
First: A lot of people today prefer to "reuse" code for different reasons. This may be because of saving time or not knowing how to do it themselves. Nim is a tool which has the possibility to be even to powerful. It could explode. This is why I bug @Araq about the creation of an official "Nim Cookbook" where such techniques get stored and explained. This get lost in the Forums quick.
Second: I believe that people tend to believe that a standard library implementation is "magically the best and fastest way". Adding "visible" code feels to them "slower" than using a proc from another lib. This for sure is partly the interpreter sickness.
Third: This is Nim and Nim is not V1.0 and the standard libs are neither. There may be tasks which are important enough to get included into the standard library.
A Nim cookbook would be amazing. There are loads of example tasks that could already be included from http://rosettacode.org/wiki/Category:Nim too.
EDIT: I also feel like a place to show the power of small templates or simple macros would be great at showing off how you can do things differently in Nim.
write a Nim book!
Do you know what you are asking for?
Writing a good Nim book is 1000 hours of hard work for one of the smart Nim developers. And if they intent to sell it at all, they may sell one hundred pieces and get a few Euro or Dollar per piece!