www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - trying to understand in, inout, and ref...

reply mark <mark qtrac.eu> writes:
I have these code snippets:

alias WordList = string[];
alias WordSet = int[string]; // key = word; value = 0

WordList generate(WordSet allWords, int steps) {
     WordList ladder; // will be changed in update()
     auto words = allWords.dup; // will be changed in update()
     auto compatibles = allWords.dup; // will be changed in this 
function
     auto prev = update(ladder, words, compatibles);
     // TODO
     return ladder;
}

string update(WordList ladder, WordSet words, WordSet 
compatibles) {
     auto word = ""; // TODO random string from compatibles
     // TODO remove word from words; add word to ladder
     return word;
}

Regarding generate(): allWords should never be changed (generate 
is called in a loop with the same allWords every time) -- so 
should it be `in WordSet allWords`?

Regarding update(): ladder and words are both modified in update 
-- so should they be `ref WordList ladder` and `ref WordSet 
words`? And if so, do I need to change the update() call in the 
generate() function? compatibles is (will be) modified in 
generate() but not in update(), so should it be `in WordSet 
compatibles`?
Jan 22 2020
parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 22 January 2020 at 10:49:07 UTC, mark wrote:
 Regarding generate(): allWords should never be changed 
 (generate is called in a loop with the same allWords every 
 time) -- so should it be `in WordSet allWords`?
For parameters that shouldn't be changed, use const. So it should be `const WordSet allWords`.
 Regarding update(): ladder and words are both modified in 
 update -- so should they be `ref WordList ladder` and `ref 
 WordSet words`? And if so, do I need to change the update() 
 call in the generate() function?
Yes, they should be `ref`. No, you do not have to change the update() call.
 compatibles is (will be) modified in generate() but not in 
 update(), so should it be `in WordSet compatibles`?
It should be `const WordSet compatibles`.
Jan 22 2020
parent reply mark <mark qtrac.eu> writes:
On Wednesday, 22 January 2020 at 14:23:53 UTC, Paul Backus wrote:
 On Wednesday, 22 January 2020 at 10:49:07 UTC, mark wrote:
 Regarding generate(): allWords should never be changed 
 (generate is called in a loop with the same allWords every 
 time) -- so should it be `in WordSet allWords`?
For parameters that shouldn't be changed, use const. So it should be `const WordSet allWords`.
That single change produces (using LDC 1.19.0 - D 2.089.1): ./wordladder.d(52): Error: function wordladder.update(string[] ladder, int[string] words, const(int[string]) compatibles) is not callable using argument types (string[], const(int)[string], const(int)[string]) ./wordladder.d(52): cannot pass argument words of type const(int)[string] to parameter int[string] words Failed: ["/home/mark/opt/ldc2-1.19.0-linux-x86_64/bin/ldmd2", "-v", "-o-", "./wordladder.d", "-I."] So I've rolled it back.
 Regarding update(): ladder and words are both modified in 
 update -- so should they be `ref WordList ladder` and `ref 
 WordSet words`? And if so, do I need to change the update() 
 call in the generate() function?
Yes, they should be `ref`. No, you do not have to change the update() call.
I've done this but my impression from the docs is that passing slices and associative arrays is already done by reference so these aren't needed? (I can't tell yet because I haven't written the modifying code.)
 compatibles is (will be) modified in generate() but not in 
 update(), so should it be `in WordSet compatibles`?
It should be `const WordSet compatibles`.
Done that and it compiles fine. Thanks.
Jan 22 2020
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 22 January 2020 at 15:26:06 UTC, mark wrote:
 I've done this but my impression from the docs is that passing 
 slices and associative arrays is already done by reference so 
 these aren't needed?
They are pointers passed by value. If you're familiar with C, think of passing struct Array { size_t length; element* ptr; } The elements are passed by ref there; they aren't copied to the function and any changes to them will be visible outside. BUT if you change the length of it or reallocate it in any way those changes are NOT seen outside. So with AAs and slices, if you just want to work with existing elements, no need for ref. But if you are going to do any kind of resizing - adding or removing elements - ref is likely what you want.
Jan 22 2020
next sibling parent mark <mark qtrac.eu> writes:
On Wednesday, 22 January 2020 at 15:33:44 UTC, Adam D. Ruppe 
wrote:
 On Wednesday, 22 January 2020 at 15:26:06 UTC, mark wrote:
 I've done this but my impression from the docs is that passing 
 slices and associative arrays is already done by reference so 
 these aren't needed?
They are pointers passed by value. If you're familiar with C, think of passing struct Array { size_t length; element* ptr; } The elements are passed by ref there; they aren't copied to the function and any changes to them will be visible outside. BUT if you change the length of it or reallocate it in any way those changes are NOT seen outside. So with AAs and slices, if you just want to work with existing elements, no need for ref. But if you are going to do any kind of resizing - adding or removing elements - ref is likely what you want.
Thanks - that's exactly what I needed to know! (I'm also very much enjoying your D Cookbook.)
Jan 22 2020
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/22/20 10:33 AM, Adam D. Ruppe wrote:

 
 BUT if you change the length of it or reallocate it in any way those 
 changes are NOT seen outside.
 
 So with AAs and slices, if you just want to work with existing elements, 
 no need for ref. But if you are going to do any kind of resizing - 
 adding or removing elements - ref is likely what you want.
So just to clarify this a bit for AAs. Adding or removing elements from an AA DOES get seen outside, even if you don't pass by ref, except for one case -- the AA is in its initial state. The reason is because an AA is actually a pointer-to-implementation (pImpl) struct, which is initialized to null, but allocated on first element added. So if you don't add any elements, and pass it by value, you are passing null by value, and adding elements will allocate it. But the result doesn't get seen back at the parameter you passed. However, if you add one element, and then pass it, the implementation is already allocated and does not change locations. So then you can add more elements even if you pass by value, and the implementation stays at the same location. example: void foo(int[int] p) { p[1] = 1; p[2] = 2; } int[int] aa; foo(aa); assert(aa.length == 0); // new allocation in foo not seen aa[0] = 0; // first initialization, no longer null foo(aa); assert(aa.length == 3); // now, you see the changes aa.clear(); // remove all elements, but don't deallocate. assert(aa.length == 0); foo(aa); assert(aa.length == 2); // was already preallocated, so impl stays the same. This aspect is very confusing to many people. -Steve
Jan 22 2020