Re: Why not allow elementwise operations on tuples?
On Wednesday, 18 January 2023 at 16:42:00 UTC, JG wrote: I guess such a method wouldn't be particularly generic since a tuple does not need to consist of types that have the same operations e.g. Tuple!(int,string) etc That's where `areCompatibleTuples` function comes in!
Re: Why not allow elementwise operations on tuples?
On Friday, 13 January 2023 at 15:27:26 UTC, H. S. Teoh wrote: On Fri, Jan 13, 2023 at 02:22:34PM +, Sergei Nosov via Digitalmars-d-learn wrote: Hey, everyone! I was wondering if there's a strong reason behind not implementing elementwise operations on tuples? Say, I've decided to store 2d points in a `Tuple!(int, int)`. It would be convenient to just write `a + b` to yield another `Tuple!(int, int)`. I've written a Vec type that implements precisely this, using tuples behind the scenes as the implementation, and operator overloading to allow nice syntax for vector arithmetic. Yeah, that's clear that such an implementation is rather straightforward. Although, I'm a bit confused with your implementation - 1. it doesn't seem to use tuples behind the scenes despite your claim (it uses static array) 2. `alias impl this;` introduces some unexpected interactions (e.g. `~` and `toString` are "intercepted" by the array implementation and yield "wrong" results). Anyway, my original question was primarily about reasoning - why there's no implementation specifically for `std.Tuple`? If it's a "feature, not a bug" - what's the best way to provide an implementation on the client side?
Why not allow elementwise operations on tuples?
Hey, everyone! I was wondering if there's a strong reason behind not implementing elementwise operations on tuples? Say, I've decided to store 2d points in a `Tuple!(int, int)`. It would be convenient to just write `a + b` to yield another `Tuple!(int, int)`. I can resort to using `int []` arrays and write elementwise operations as `c[] = a[] + b[]` which is almost fine - but it uses dynamic allocation and forces the user to create an explicit destination variable. It seems a bit awkward given that it's fairly straightforward to write smth as ``` T opBinary(string op, T)(T lhs, T rhs) if (isTuple!T) { T result; static foreach (i; 0 .. T.Types.length) { mixin("result.field[i] = lhs.field[i]"~op~"rhs.field[i];"); } return result; } ``` You only need to turn it into a member function to make it work. You can even make it more general and allow such operations for different, but compatible tuple types (there's a function `areCompatibleTuples` to check for such compatibility). Yet, there's only a specialization for tuple concatenation of `opBinary` (and an implementation of `opCmp` and `opAssign`). So, to repeat the question - is this a deliberate decision to not implement the default elementwise operation?
Re: Can you simplify nested Indexed types?
On Tuesday, 27 December 2022 at 16:43:49 UTC, Ali Çehreli wrote: On 12/27/22 07:09, Sergei Nosov wrote: If what you are looking for is a way of defining a variable for "any InputRange that produces a specific type (size_t in this case)", then there is inputRangeObject, which uses OOP: https://dlang.org/phobos/std_range_interfaces.html#InputRange I have an additional example here: http://ddili.org/ders/d.en/ranges_more.html#ix_ranges_more.inputRangeObject Ali Thanks, everyone! I guess, this answer is the closest to what I was looking for. Somehow, I missed the range interfaces (and was considering to use `Variant` or smth). It does seem to answer the original question, albeit with layer(s) of indirection. ``` auto indicies = iota(3); RandomAccessFinite!int ai = indexed(a, indicies).inputRangeObject; ai = indexed(ai, iota(2)).inputRangeObject; ``` Still, my gut feel is that some compile-time solution is possible - will, probably, tinker with it for a little more. Why not use filter(), isn't it important to filter out what's in range? That does something different. Well, pretty sure this isn't what you meant by "same variable" but since it technically does what you want, I decided to share it: Basically I'm abusing array and this thing might be pretty memory heavy... Yeah, using arrays is another alternative, but as you mention, it uses more memory and makes index evaluation eager (vs lazy).
Re: Can you simplify nested Indexed types?
On Tuesday, 27 December 2022 at 15:20:24 UTC, Salih Dincer wrote: On Tuesday, 27 December 2022 at 15:09:11 UTC, Sergei Nosov wrote: Consider, I have the following code: ```d auto a = [3, 6, 2, 1, 5, 4, 0]; auto indicies = iota(3); auto ai = indexed(a, indicies); //ai = indexed(ai, iota(2)); writeln(ai); ``` I confuse about comment line that I mark... SDB@79 Not sure I'll be more helpful, but I'll try to add more details. I have an array and I use `indexed` on it. Conceptually, I now have a second array, but it doesn't exist in memory explicitly - only a function to map indicies from "second array" to "first array" is stored; all the values are stored once - in the "first array". Now, I want to have third array that will do the same trick with the second array. The problem is that the second array is not really an array (but, conceptually, it is an array with random access). If I create a new variable with `auto` as type - obviously, it works. But can I use the same variable I used to store the "second array"? (In the provided code that doesn't work because of the type mismatch).
Re: Function to print a diamond shape
On Thursday, 20 March 2014 at 21:25:03 UTC, Ali Çehreli wrote: This is a somewhat common little exercise: Write a function that takes the size of a diamond and produces a diamond of that size. When printed, here is the output for size 11: * *** * *** * *** * *** * *** * What interesting, boring, efficient, slow, etc. ways are there? Ali Probably, the most boring way is foreach(i; 0..N) { foreach(j; 0..N) write( *[i + j = N/2 i + j 3*N/2 i - j = N/2 j - i = N/2]); writeln; }
Re: Function to print a diamond shape
On Friday, 21 March 2014 at 13:59:27 UTC, Vladimir Panteleev wrote: On Friday, 21 March 2014 at 12:32:58 UTC, Sergei Nosov wrote: On Thursday, 20 March 2014 at 21:25:03 UTC, Ali Çehreli wrote: This is a somewhat common little exercise: Write a function that takes the size of a diamond and produces a diamond of that size. When printed, here is the output for size 11: * *** * *** * *** * *** * *** * What interesting, boring, efficient, slow, etc. ways are there? Ali Probably, the most boring way is foreach(i; 0..N) { foreach(j; 0..N) write( *[i + j = N/2 i + j 3*N/2 i - j = N/2 j - i = N/2]); write( *[abs(i-N/2) + abs(j-N/2) = N/2]); writeln; } Beat me. Yours is even more boring. =)
Just-run-the-unittests
Hi! Suppose I have a small .d script that has a main. Is there any simple way to just run the unit tests without running main at all? I thought -main switch was intended for this, but apparently it works only if there's no main defined at all, otherwise, it issues a double main definition error. I could place main into a separate module but its really awkward to create 2 files for a small script.
Re: Just-run-the-unittests
On Sunday, 16 March 2014 at 08:22:04 UTC, safety0ff wrote: On Sunday, 16 March 2014 at 07:59:33 UTC, Sergei Nosov wrote: Hi! Suppose I have a small .d script that has a main. Is there any simple way to just run the unit tests without running main at all? Here's the first thing that came to mind: If you never want to both unit tests and regular main: code begins import std.stdio; version(unittest) void main(){} else void main() { writeln(Hello world!); } unittest { writeln(Hello unit testing world!); } code ends If you sometimes want to have your normal main with unit testing you can replace version(unittest) with version(nopmain) or some other custom version identifier and compile with -version=nopmain when you want the dummy main. Thx! That's better, but I think -main switch could be made to work like 'add or replace main by stub' instead of just 'add'. I don't think it'll hurt anybody, what do you think?
Re: foreach/iota countdown
On Tuesday, 18 February 2014 at 05:21:24 UTC, Brian Schott wrote: On Monday, 17 February 2014 at 19:22:38 UTC, simendsjo wrote: Should the following two uses be a compile-time error? foreach(i; 10 .. 0) // Never executes foreach(i; iota(10, 0)) // .. neither does this I would like the second to either be a compile-time error or automagically use a negative step. So we need to use a negative step in iota() or use a for loop foreach(i; iota(10, 0, -1)) // as expected I just added this check to DScanner: - import std.stdio; void main(string[] args) { auto x = args[3 .. 2]; foreach (i; 20 .. 10) { } } - /home/alaran/tmp/test.d(5:16)[warn]: 3 is larger than 2. This slice is likely incorrect. /home/alaran/tmp/test.d(6:22)[warn]: 20 is larger than 10. Did you mean to use 'foreach_reverse( ... ; 10 .. 20)'? Isn't foreach_reverse being deprecated?
Re: Array as an argument, ambiguous behaviour.
On Wednesday, 29 January 2014 at 13:15:30 UTC, Cooler wrote: On Wednesday, 29 January 2014 at 12:40:00 UTC, Tobias Pankrath wrote: On Wednesday, 29 January 2014 at 11:46:23 UTC, Cooler wrote: Thank you for detailed explanation. But the question is - Is that correct that language allows ambiguous behavior? Where is it ambiguous? Ambiguity is here... When I call fun1() or fun2() I know the behavior directly from function signature (read the comments in my first post). For fun3() case the caller side don't know the behavior directly from function signature. To know what will happen with array a, the caller must see to fun3() body. Ambiguity is - in first and second cases the caller knows what happens with a, but in third case the caller does not know what happens with a. I believe you encounter an array reallocation. If fun3 doesn't change the size of the array - you will see every change made by fun3 to the contents of `a` (but the `a` itself cannot be changed - only the contents). No other way around. If, however, in fun3 you change the size of the array - it may reallocate. Like, if you're appending to `x` - it will allocate a new array and make x point to it. Now `a` and `x` point to distinct arrays. And any change you do using `x` won't be seen by `a`. And, yes, this is the intended behavior.
Re: Array as an argument, ambiguous behaviour.
On Wednesday, 29 January 2014 at 15:11:33 UTC, Cooler wrote: Yes, that is how slices work in D. The following article explains the non-determinism that you mention: http://dlang.org/d-array-article.html Ali Thank you for the article. Quotation from the article It is a good idea to note in the documentation how the passed in slice might or might not be overwritten. May be just prohibit at language level the case of fun3() function, to do not allow unpredictable behavior? This behavior is just a consequence of the deliberate decision on how arrays should be implemented. Any decision would be a trade-off. Like, if you just disallow this signature, you will have to use .dup at the caller side if you want the semantics of fun3. And often this copy might be unnecessary. It's really like a ball under the carpet. You make it flat in one place, but the ball pops up in the other. The trade-off that D chooses is pretty reasonable. You just have to accept that and get used to it.
Re: enum value vs. immutable
On Monday, 2 December 2013 at 07:31:56 UTC, Sergei Nosov wrote: On Monday, 2 December 2013 at 05:57:33 UTC, CJS wrote: I was reading the enum page of Ali Çehreli's (excellent) D book (http://ddili.org/ders/d.en/enum.html), and I'm confused by an enum value (not enum type), such as enum secondsPerDay = 60 * 60 * 24; In that situation I would have used an immutable variable. Is there any reason to prefer enum vs. immutable when defining constants? enum is a compile-time constant and an immutable variable is not. As an example, in order to create a enum variable you have to know it's value at compile time, e.g. you can't read it from file. On the contrary, you can read a string from file and string is the same thing as immutable(char) in D. Typo - the string is the same thing as immutable(char)[] in D (note the rectangular braces)
Re: enum value vs. immutable
On Monday, 2 December 2013 at 05:57:33 UTC, CJS wrote: I was reading the enum page of Ali Çehreli's (excellent) D book (http://ddili.org/ders/d.en/enum.html), and I'm confused by an enum value (not enum type), such as enum secondsPerDay = 60 * 60 * 24; In that situation I would have used an immutable variable. Is there any reason to prefer enum vs. immutable when defining constants? enum is a compile-time constant and an immutable variable is not. As an example, in order to create a enum variable you have to know it's value at compile time, e.g. you can't read it from file. On the contrary, you can read a string from file and string is the same thing as immutable(char) in D.
Re: Function literal bug?
On Thursday, 28 November 2013 at 08:23:22 UTC, bearophile wrote: Global structs don't need the static attribute. I've thought so, but added it just as a I really mean, that I don't need context. This version of your code gives the output 6 6 on Windows 32 bit: Do you have a 64-bit OS at hand? I don't know why in A2 it infers a delegate. There's at least 2 of us.
Re: Function literal bug?
On Thursday, 28 November 2013 at 10:23:39 UTC, Kenji Hara wrote: It's a known front-end issue. https://d.puremagic.com/issues/show_bug.cgi?id=11545 Kenji Hara Great! Does this pull resolve both issues? (correct length and x=x syntax)
Function literal bug?
Hi! This is kind of bug report/question. I'm running Ubuntu 12.04 (x64), DMD v2.064.2 and have the following code: T identity(T)(T e) { return e; } struct S(alias Func) { void call() { import std.stdio; writeln(Func(string).length); } } static struct S1 { alias S!(identity) A1; //alias S!(x = x) A2; alias S!(function string (string e) { return e; }) A3; } void main() { S1.A1.init.call(); S1.A3.init.call(); } The main complaint is that function literal is somehow broken in that case. The output of the program is 6 4527264 For some reason, length in the second case is wrong. Another question (which I believe is reasonable) is that I cannot use the (x = x) syntax for function literal (the commented alias A2). The compilation error is: tmp.d(15): Error: delegate tmp.S1.__lambda4!string.__lambda4 function literals cannot be class members tmp.d(8): Error: template instance tmp.S1.__lambda4!string error instantiating tmp.d(15):instantiated from here: S!((x) = x) tmp.d(8): Error: this for __lambda4 needs to be type S1 not type S!((x) = x) I assume, compiler infers the type of the literal to be delegate, but it's clearly not the case, since I don't need no context here. I appreciate any comments.
Re: What does it mean void[]?
On Friday, 15 November 2013 at 09:19:04 UTC, Orfeo wrote: I have found in the module https://github.com/NCrashed/serial-port/blob/master/source/serial/device.d this function: void write(const(void[]) arr) { ... What exactly is void[]? An array of pointers? An array of anything? Thank you It's semantics is an array of octets. Similar, to void * in C++, except, the overall length in bytes is known.
alias template parameter
Hi! I've been thinking about how alias template parameters work and I'm really confused =) It makes perfect sense for literals, names, etc. But what I can't get is how does it work for delegates. If I have a function auto apply(alias fun, T...)(T args) { return fun(args); } And then I have int y = 2; apply!(x = y)(1); How in the world does this work? Is the context address known at compile-time?
shared vs __gshared
Hi! I'm puzzled with what's the difference between shared and __gshared. Until now I've (mistakenly) considered that shared variables are free from low-level data races. I.e. the operations with shared data are atomic. And that __gshared is the usual (in C++ sense) shared data. So, in my understanding, it was that if I do shared int s = 0; void inc() { s++; } void main() { foreach (i; iota(100_000).parallel) s++; } I will always get s == 100_000 by the end of foreach loop. And if I use __gshared specifier, I will get the undefined value less or equal 100_000. But now I've run the test and I get the undefined value result for both shared and __gshared. I've looked this up in TDPL, and careful reading got me that shared guarantees only that the result of a write is instantly visible to all threads. So according to this, I can load the value in register, change it there, wait until someone else rewrites the value, and put my version on top, discarding someone else's result. Is this how shared is supposed to work? If so, how is the __gshared different from it? Does it not guarantee that the result of a write is instantly visible to all threads? If so, does this difference really matter?
Re: shared vs __gshared
Thank you for answers. Let me check if I got this right. On Monday, June 10, 2013 13:23:26 Steven Schveighoffer wrote: shared was supposed to infer memory barriers, but AFAIK, it doesn't do that. Not sure it ever will. So, my first impression about what shared should do (no low-level races at all) was correct, but the things didn't work out that way. So that kind of doesn't solve the issue with low-level races, which IIRC Andrei considers the biggest crime a language type system can commit. And the likely (brand-new) solution to that is On Monday, 10 June 2013 at 14:49:27 UTC, Dmitry Olshansky wrote: Now there was a discussion on it recently which indicates that shared data might lose it's built-in ops to prevent confusion and require folks to just use core.atomic directly for lock-free or alternatively cast+mutex for lock-based. which seems reasonable too. So, is my understanding correct? If yes, why the path with memory barriers was announced, but not taken?
Failed to sort range
Hi! I'm trying to implement an array, which uses malloc to allocate memory. Also, I want to implement a random access range interface for it. That went pretty well, until I tried to sort it. Sorting function asserted Failed to sort range of type Array!(int). I've spent quite some time trying to figure out what's going on with no success. The implementation can be found at: https://gist.github.com/snosov1/5662471 I used DMD64 D Compiler v2.062 and LDC - the LLVM D compiler (trunk): based on DMD v2.062 and LLVM 3.2svn on Ubuntu. Phobos version was the one that came with the dmd compiler. Does anyone have any ideas what's wrong with the code I've provided?
Re: Failed to sort range
On Tuesday, 28 May 2013 at 12:57:12 UTC, Sergei Nosov wrote: Hi! I'm trying to implement an array, which uses malloc to allocate memory. Also, I want to implement a random access range interface for it. That went pretty well, until I tried to sort it. Sorting function asserted Failed to sort range of type Array!(int). I've spent quite some time trying to figure out what's going on with no success. The implementation can be found at: https://gist.github.com/snosov1/5662471 I used DMD64 D Compiler v2.062 and LDC - the LLVM D compiler (trunk): based on DMD v2.062 and LLVM 3.2svn on Ubuntu. Phobos version was the one that came with the dmd compiler. Does anyone have any ideas what's wrong with the code I've provided? Forgot to mention, that my hand-made sorting function (simply a copy-paste of some quicksort implementation that uses array indexing syntax) works just fine void qSort(alias less, Range)(Range A, int low, int high) { int i = low; int j = high; auto x = A[(low+high)/2]; do { while(less(A[i], x)) ++i; while(less(x, A[j])) --j; if(i = j){ auto temp = A[i]; A[i] = A[j]; A[j] = temp; i++; j--; } } while(i j); if(low j) qSort!less(A, low, j); if(i high) qSort!less(A, i, high); }
Re: Failed to sort range
On Tuesday, 28 May 2013 at 13:41:19 UTC, bearophile wrote: Sergei Nosov: That went pretty well, until I tried to sort it. Sorting function asserted Failed to sort range of type Array!(int). If you take a look a the implementation of Phobos sort, you see there is a recently added runtime test mostly meant to catch wrongly implemented comparison functions, like q{a = b} instead of q{a b}. Maybe that's the one that has fired. But I don't know why. Bye, bearophile I don't think that is the case, since I use the default comparison function for ints.
Re: Failed to sort range
Thx, Ali! 1) First, an observation: This Array design conflates the concepts of container and range. If there are actual elements that are being stored (as opposed to elements being generated), it is better tha a range merely provides access to those elements. popFront should consume the range, not the container. (Unless it is some special type of range with the responsibility of removing elements from the container.) I'm not sure I understand this correctly. Do you mean it's a good idea to separate storage and access (via range) to the container? Like std.container's containers (heh) have nested Range struct? 2) As a minor comment, back usually means the last element but your back_ has the meaning of one-past-the-last element. Yeah, that's probably a not-so-good name. 3) You have to rethink the indexing as well. opIndex indexes directly on vec_: ref T opIndex(size_t idx) { return vec_[idx]; } However, we may be on a slice which has already been sliced before (as is the case in quick sort, which SwapStrategy.unstable uses). So, I think opIndex should add front_ to idx: ref T opIndex(size_t idx) { return vec_[front_ + idx]; } It is a start but still not the solution. Sorry... :/ That's obviously a bug, thanks. But, yeah, not the last one =) I updated the gist. And also, replaced the malloc call with new. The behavior is the same.
Re: Failed to sort range
On Tuesday, 28 May 2013 at 18:38:01 UTC, Ali Çehreli wrote: On 05/28/2013 11:31 AM, Ali Çehreli wrote: @property Array!T opSlice(size_t i, size_t j) { // ... ret.front_ = i; I feel like the initialization of front_ above is not right either. Imagine quick sort where we are slicing the right-hand side of a range as [0..10], which has already been sliced before. Setting front_ to 0 would be wrong because then opIndex would still be counting from the beginning of the original elements. My explanation is wrong but I think there is still a bug. Imagine we are in the right-hand range that has been sliced as [10..$]. Now front_ is 10 and all is good: s[0] provides the arr[10] of the original array. Now imagine our slice again by [5..$]. s_further[0] should provide arr[15] of the original array but your setting front_ to 5 would unfortunately provide arr[5]. Ali Yes, exactly. I believe, the same fix should be applied here: front_ + i, front_ + j. It passes the sorting test. Thank you so much, Ali. Feels like to be back in school again.
Re: Failed to sort range
On Tuesday, 28 May 2013 at 20:43:32 UTC, Ali Çehreli wrote: On 05/28/2013 12:47 PM, Anthony Goins wrote: sort!(ab, SwapStrategy.stable)(arr); This worked for me with the code at your link. I've noticed that too. The reason that works is because in that case it uses Tim Sort. Apparently, the Tim Sort algorithm does not expose the bugs that were in the code. Ali I believe the issues with opIndex and opSlice caused the bug. Now, those are fixed, and I guess it's safe to say that range interface is correct. Although, I remember a discussion in the NG about somewhat standardized testing facilities for ranges ( http://forum.dlang.org/thread/20130321130858.3ef4@unknown ). Precisely the semantic/runtime behavior, to supplement isSomeRange templates family. Do you, guys know, was there any activities on it?
Re: Possible bug
On Tuesday, 26 March 2013 at 05:40:00 UTC, Steven Schveighoffer wrote: What you have to do is instantiate the template, then call the constructor: S!(int, 4).S!(byte, 5)(3) Note that the way templates work in D, a templated struct is really a shortcut for: template S(T) { struct S { ... } } So when you instantiate it, S!(int)(5) is really shorthand for S!(int).S(5). Every template is this way. The shorthand version calls on the eponymous member implicitly, that is, the member with the same name as the template. Currently, this only works if there is exactly one member. For example, a function template is really: template foo(T) { void foo(T t) {...} } So doing: foo!(int)(1) is really the same as: foo!(int).foo(1) It's all outlined here: http://dlang.org/template.html search for Implicit Template Properties -Steve Thx a lot!
Possible bug
Hi! This code doesn't compile with dmd v2.062 on Linux_x86_64 pre struct test(T) { T *data_; this(T *data) { data_ = data; } } void main() { int *cptr = null; test!int hello = test(cptr); } /pre Error: dmd test.d test.d(12): Error: struct test.test does not match any function template declaration. Candidates are: test.d(2):test.test(T) test.d(12): Error: struct test.test(T) cannot deduce template function from argument types !()(int*) Everything's fine if I specify parameters explicitly: pre test!int hello = test!int(cptr); /pre
Re: Possible bug
On Monday, 25 March 2013 at 14:12:17 UTC, bearophile wrote: Sergei Nosov: Everything's fine if I specify parameters explicitly: pre test!int hello = test!int(cptr); /pre Some persons have proposed alternative designs, but D is working as currently designed here... Unlike template functions, templated structs don't infer the type. Bye, bearophile Thx, is there any good rationale?
Re: Possible bug
Thank you, guys! You made the matters clear to me! It would be an interesting enhancement over C++. Although, Steven, I didn't quite understand what you're suggesting to use in case of templated-struct-templated-constructor Explicitly specifying struct parameters is ok. Deducing constructor parameters from arguments is also ok. But what if not all of the constructor parameters may be deduced? E.g. one of them is an int? Would it be a no-no case? or should we merge the lists in one? or should we use two bangs? S!(int, 4)!(byte, 5)(3) ? =)