| Thread overview | |||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
February 07, 2011 Another Phobos2 test | ||||
|---|---|---|---|---|
| ||||
I've found another taks in the rosettacode.org site: http://rosettacode.org/wiki/Tic-tac-toe The program itself is not so interesting, and probably there are better ways to implement a Tic-tac-toe player program. But it's a good enough example to test Phobos2, to see how much handy it is when you compare it to the original Python version. The original Python3 code was first translated to Python2.x, then translated to D2. The translation to D was a bit of pain, for many things there was a need to do some experiments to find a syntax that works. This is bad. Some stats: - Python2 source code: 91 lines, 2385 bytes. - D2 source code: 134 lines, 3620 bytes. - D2 binary, default compilation: 338 KB on Windows with DMD. Here are small parts of the Python2 version followed by the D2 translation, sometimes followed by some comments. The D translation may be improved, if you see something, feel free to comment. ---------------------------- ''' Tic-tac-toe game player. Input the index of where you wish to place your mark at your turn. ''' import random /** Tic-tac-toe game player. Input the index of where you wish to place your mark at your turn. */ module tic_tac_toe; import std.stdio, std.random, std.string, std.algorithm, std.array, std.typecons, std.conv; The D version requires many more imports because Python has a tons of built-in features. The comment in the Python version is a docstring of the module, about as in D. But in Python you are able to access the docstrings from the code itself (see below the print __doc__), this allows to write those comments just once in the Python program, while they are written twice in the D code. I'd like the DMD compiler to be able to produce a JSON tree out of the current module during compilation, through a library, and allow the normal code to use it at compile time. So with a library you are able to extract any kind of data about the module and its code. This will be a good material (a way to perform static introspection) to later build a user-defined attributes feature on. All the Python2.x and D2 code shown is tested, unless otherwise specified. ---------------------- board = list('123456789') wins = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)) alias char[] TBoard; TBoard board = "123456789".dup; enum WINS = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]]; An alias is not as good as a typedef, to make statically sure the various functions do take a board and nothing else as input. ---------------------- def print_board(): print '\n-+-+-\n'.join('|'.join(board[x:x+3]) for x in (0, 3, 6)) auto print_board() { string[] parts; foreach (x; [0, 3, 6]) parts ~= array(map!(to!string)(board[x .. x+3])).join("|"); writeln(parts.join("\n-+-+-\n")); } I'd like join to work with a lazy range and on chars too, allowing code like (untested): auto print_board() { string[] parts; foreach (x; [0, 3, 6]) parts ~= board[x .. x+3].join("|"); writeln(parts.join("\n-+-+-\n")); } This means: join("123", "x") ==> "1x2x3" And: join(map!(to!string)([1,2,3]), "x") ==> "1x2x3" See: http://d.puremagic.com/issues/show_bug.cgi?id=4468 http://d.puremagic.com/issues/show_bug.cgi?id=5542 ---------------------- def score(board=board): for w in wins: b = board[w[0]] if b in "XO" and all(board[i] == b for i in w): return (b, [i+1 for i in w]) return None auto score(TBoard sboard=board) { foreach (w; WINS) { auto b = sboard[w[0]]; bool found = canFind!((i){ return sboard[i] != b; })(w); if ((b == 'X' || b == 'O') && !found) { return new Tuple!(char, int[])(b, array(map!q{a+1}(w))); } } return cast(typeof(return))null; } This function has required several tries to be written. That final cast allows code to be more DRY, and it comes from some experiments. Here we have a "auto" return type and a typeof(return) in the same function. There is no clean way to create a tuple on the heap, the way I have used is the best I have found. I'd like Python simple functions all() and any() in Phobos2 too. With it you are able to write code a bit less ugly like (untested): auto score(TBoard sboard=board) { foreach (w; WINS) { auto b = sboard[w[0]]; if ((b == 'X' || b == 'O') && all!((i){ return board[i] == b; })(w)) { return new Tuple!(char, int[])(b, array(map!q{a+1}(w))); } } return cast(typeof(return))null; } Compared to the original Python code this D2 code is not so good still. See: http://d.puremagic.com/issues/show_bug.cgi?id=5544 ---------------------- def finished(): return all(b in 'XO' for b in board) auto finished() { return reduce!q{a && b}(true, map!q{a == 'X' || a == 'O'}(board)); } This D2 code comes from some experiments (while the Python code often is correct at my first try, because it's more clean, less noisy and it's usually much less fragile). If Phobos2 adds an all() it becomes (untested), that's much better: auto finished() { return all!q{a == 'X' || a == 'O'}(board); } Of course the introduction of lazy & eager sequence comphrensions like Python ones allows to reduce the need of lot of map, filter, etc. ---------------------- def space(board=board): return [b for b in board if b not in "XO"] auto space(TBoard sboard=board) { return to!(char[])(array(filter!q{a != 'X' && a != 'O'}(sboard))); } That to!(char[]) was necessary because here array() returns an array of dchar... This is not a so nice thing. My suggestion to avoid this quite bad situation is to look at sboard, it's a char[] and not a string. So a solution to this messy situation is to make string a strong typedef. So immutable(char)[] and string become two different types and then lazy HOFs like filter() are able to tell them apart, and act differently on them (returning a char[] if the input of a filter is a char[] and returning a dstring if the input of a filter is a string?). There are many situations where I'd like afilter() === array(filter()) and amap() == array(map()). ---------------------- def my_turn(xo, board): options = space() choice = random.choice(options) board[int(choice)-1] = xo return choice auto my_turn(char xo, TBoard sboard) { auto options = space(); char choice = options[uniform(0, options.length)]; sboard[to!int(choice~"") - 1] = xo; return choice; } to!int(choice) doesn't do what you think it does, it converts the choice char to its integer representation: import std.conv: to; void main() { assert(to!int("1") == 1); assert(cast(int)'1' == 49); assert(to!int('1') == 49); } But I think this may be better: to!int('1') === to!int("1") I'd like a _very_ handy std.random.choice(), that allows to write code like (untested): auto my_turn(char xo, TBoard sboard) { auto options = space(); char c = choice(options); sboard[to!int(c ~ "") - 1] = xo; return c; } See for the choice(): http://d.puremagic.com/issues/show_bug.cgi?id=4851 If also to!int('1') == 1 the code becomes (untested): auto my_turn(char xo, TBoard sboard) { auto options = space(); char c = choice(options); sboard[to!int(c) - 1] = xo; return c; } See for the to!int: http://d.puremagic.com/issues/show_bug.cgi?id=5543 ------------------------ def my_better_turn(xo, board): """Will return a next winning move or block your winning move if possible""" ox = 'O' if xo == 'X' else 'X' one_block = None options = [int(s)-1 for s in space(board)] for choice in options: brd = board[:] brd[choice] = xo if score(brd): break if one_block is None: brd[choice] = ox if score(brd): one_block = choice else: choice = one_block if one_block is not None else random.choice(options) board[choice] = xo return choice+1 /// Will return a next winning move or block your winning move if possible auto my_better_turn(char xo, TBoard sboard) { auto ox = xo == 'X' ? 'O' : 'X'; int one_block = -1; auto options = array(map!((s){ return s-'0'-1; })(space(sboard))); int choice; bool unbroken = true; foreach (c; options) { choice = c; auto brd = sboard.dup; brd[choice] = xo; if (score(brd)) { unbroken = false; break; } if (one_block == -1) { brd[choice] = ox; if (score(brd)) one_block = choice; } } if (unbroken) choice = one_block != -1 ? one_block : options[uniform(0, options.length)]; sboard[choice] = xo; return choice + 1; } In Python both for and while loops have an optional "else" clause, that runs if the loop is not exited by a break. It is a handy thing, that allows to avoid the "unbroken" boolean I have used in D. In the map at the top I have had to use a non portable: s-'0'-1 because s is a dchar, so you can't currently join it to a "" literal... ------------------------ def your_turn(xo, board): options = space() while True: choice = raw_input("\nPut your %s in any of these positions: %s " % (xo, ''.join(options))).strip() if choice in options: break print "Whoops I don't understand the input" board[int(choice)-1] = xo return choice auto your_turn(char xo, TBoard sboard) { auto options = space(); string choice; while (true) { writef("\nPut your %s in any of these positions: %s ", xo, options); choice = readln().strip(); if (choice.length == 1 && std.algorithm.indexOf(options, choice[0]) != -1) break; writeln("Whoops I don't understand the input"); } sboard[to!int(choice) - 1] = xo; return choice; } Here choice in options becomes (choice.length == 1 && std.algorithm.indexOf(options, choice[0]) != -1) Because D2 refused to use a handy "in" for strings, this is awful. I have had to use indexOf with module qualification to avoid errors. Regarding raw_input() see: http://d.puremagic.com/issues/show_bug.cgi?id=4716 ------------------------ def me(xo='X'): print_board() print '\nI go at', my_better_turn(xo, board) return score() def you(xo='O'): print_board() # Call my_turn(xo, board) below for it to play itself print '\nYou went at', your_turn(xo, board) return score() print __doc__ while not finished(): s = me('X') if s: print_board() print "\n%s wins along %s" % s break if not finished(): s = you('O') if s: print_board() print "\n%s wins along %s" % s break else: print '\nA draw' auto me(char xo='X') { print_board(); writeln("\nI go at ", my_better_turn(xo, board)); return score(); } auto you(char xo='O') { print_board(); // Call my_turn(xo, board) below for it to play itself writeln("\nYou went at ", your_turn(xo, board)); return score(); } void main() { writeln("Tic-tac-toe game player."); writeln("Input the index of where you wish to place your mark at your turn."); bool unbroken = true; while (!finished()) { auto s = me('X'); if (s) { print_board(); writefln("\n%s WINS along %s", s.tupleof); unbroken = false; break; } if (!finished()) { s = you('O'); if (s) { print_board(); writefln("\n%s WINS along %s", s.tupleof); unbroken = false; break; } } } if (unbroken) writeln("\nA draw"); } The print __doc__ is replaced by two writeln. There is another usage of unbroken. ------------------------ Bye, bearophile | ||||
February 07, 2011 Re: Another Phobos2 test | ||||
|---|---|---|---|---|
| ||||
Posted in reply to bearophile | My gut tells me you'd get much better results if you tried to write D in D instead of Python in D. I might take a stab at this myself. I can see lots of improvements to the original code. | |||
February 07, 2011 Re: Another Phobos2 test | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Adam Ruppe | Adam Ruppe: > My gut tells me you'd get much better results if you tried to write D in D instead of Python in D. That's really beside the point. The point of the post is that there are some spots where I'd like to see Phobos improved. (And I am willing to write part of the Phobos code I am asking for). >I might take a stab at this myself. I can see lots of improvements to the original code.< The original Python code is globally quite bad. Don't waste your time trying to improve it. Improve Phobos instead :-) Bye, bearophile | |||
February 07, 2011 Re: Another Phobos2 test | ||||
|---|---|---|---|---|
| ||||
Posted in reply to bearophile | On Monday, February 07, 2011 15:34:26 bearophile wrote:
> Adam Ruppe:
> > My gut tells me you'd get much better results if you tried to write D in D instead of Python in D.
>
> That's really beside the point. The point of the post is that there are some spots where I'd like to see Phobos improved. (And I am willing to write part of the Phobos code I am asking for).
Actually, it's not beside the point at all. Regardless of what language you're programming in, it's generally best to program in the typical paradigms of that language. Trying to contort it to act like another language is _not_ going to result in optimal code.
Now, that's not to say that Phobos can't be improved upon (it certainly can be), but if you focus too much on how it doesn't do something like some other language does, you'll miss what it _can_ do. And it's quite possible that it actually does what you're trying to do quite well if you'd just stop trying to contort it to act like another language (be it Python or Haskell or Rust or Go or Java or C++ or C or Ruby or...).
- Jonathan M Davis
| |||
February 08, 2011 Re: Another Phobos2 test | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | "Jonathan M Davis" <jmdavisProg@gmx.com> wrote in message news:mailman.1382.1297122691.4748.digitalmars-d@puremagic.com... > On Monday, February 07, 2011 15:34:26 bearophile wrote: >> Adam Ruppe: >> > My gut tells me you'd get much better results if you tried to write D in D instead of Python in D. >> >> That's really beside the point. The point of the post is that there are some spots where I'd like to see Phobos improved. (And I am willing to write part of the Phobos code I am asking for). > > Actually, it's not beside the point at all. Regardless of what language > you're > programming in, it's generally best to program in the typical paradigms of > that > language. Trying to contort it to act like another language is _not_ going > to > result in optimal code. > > Now, that's not to say that Phobos can't be improved upon (it certainly > can be), > but if you focus too much on how it doesn't do something like some other > language does, you'll miss what it _can_ do. And it's quite possible that > it > actually does what you're trying to do quite well if you'd just stop > trying to > contort it to act like another language (be it Python or Haskell or Rust > or Go > or Java or C++ or C or Ruby or...). > Using std.algorithm qualifies as contorting D? | |||
February 08, 2011 Re: Another Phobos2 test | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Nick Sabalausky | "Nick Sabalausky" <a@a.a> wrote in message news:iiq4pa$28aa$1@digitalmars.com... > "Jonathan M Davis" <jmdavisProg@gmx.com> wrote in message news:mailman.1382.1297122691.4748.digitalmars-d@puremagic.com... >> On Monday, February 07, 2011 15:34:26 bearophile wrote: >>> Adam Ruppe: >>> > My gut tells me you'd get much better results if you tried to write D in D instead of Python in D. >>> >>> That's really beside the point. The point of the post is that there are some spots where I'd like to see Phobos improved. (And I am willing to write part of the Phobos code I am asking for). >> >> Actually, it's not beside the point at all. Regardless of what language >> you're >> programming in, it's generally best to program in the typical paradigms >> of that >> language. Trying to contort it to act like another language is _not_ >> going to >> result in optimal code. >> >> Now, that's not to say that Phobos can't be improved upon (it certainly >> can be), >> but if you focus too much on how it doesn't do something like some other >> language does, you'll miss what it _can_ do. And it's quite possible that >> it >> actually does what you're trying to do quite well if you'd just stop >> trying to >> contort it to act like another language (be it Python or Haskell or Rust >> or Go >> or Java or C++ or C or Ruby or...). >> > > Using std.algorithm qualifies as contorting D? > (Although, I didn't read the OP very closely, so maybe I'm off-base.) | |||
February 08, 2011 Re: Another Phobos2 test | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Nick Sabalausky | Nick Sabalausky:
> (Although, I didn't read the OP very closely, so maybe I'm off-base.)
They are right, I have done a "strategic" error. In the original post I have mixed two kinds of very unrelated things: very small suggestions to improve (in my opinion) Phobos, plus some critiques to the D2 language compared to Python. People have rightly not appreciated my post because of the second group of useless comments. I am sorry, I am just stupid. Regarding the original post, in Bugzilla I have added only Phobos-related things.
Bye,
bearophile
| |||
February 08, 2011 Re: Another Phobos2 test | ||||
|---|---|---|---|---|
| ||||
Posted in reply to bearophile | On Monday, February 07, 2011 17:11:50 bearophile wrote:
> Nick Sabalausky:
> > (Although, I didn't read the OP very closely, so maybe I'm off-base.)
>
> They are right, I have done a "strategic" error. In the original post I have mixed two kinds of very unrelated things: very small suggestions to improve (in my opinion) Phobos, plus some critiques to the D2 language compared to Python. People have rightly not appreciated my post because of the second group of useless comments. I am sorry, I am just stupid. Regarding the original post, in Bugzilla I have added only Phobos-related things.
Stupid is going a bit far. You do seem to have a tendancy however to at least present things as if D should act like python, which isn't necessarily a good thing. Different points of view on how to go about solving problems can lead to better solutions however. It's just that it doesn't necssarily make sense for D to act like python, and you often come across like D is defficient when it does things differently than python.
- Jonathan M Davis
| |||
February 08, 2011 Re: Another Phobos2 test | ||||
|---|---|---|---|---|
| ||||
Posted in reply to bearophile | My implementation http://arsdnet.net/tictactoe.d source: 138 lines, 2420 bytes You can see the byte count is comparable to the python 2, but I have more lines. This reflects my preferences of one line = one instruction. I usually prefer "Introduction to Programming" style code than "functional code golf" style, so you see that too. I also put in a lot of contracts just because i can. I'm trying to get into the habit of that. > Input the index of where you wish to place your mark at your turn. writef("Input the index of your move (from %s): ", End user instructions have no place as documentation comments. Being able to print out documentation comments at runtime is almost useless - doing so indicates a high probability of bad comments. You can see that I used a simple string literal for this, declaring it where it is used. > I'd like the DMD compiler to be able to produce a JSON tree out of > the current module during compilation, through a library, and allow > the normal code to use it at compile time. dmd -X to generate it, then dmd -J to load it up for use at compile time. Documentation comments are already included in the json output. However, see my comment above on how I feel about the need for this... > An alias is not as good as a typedef, to make statically sure the various functions do take a board and nothing else as input. I wrote: struct Board { } If you want a separate type, make a separate type. I'm sad to see typedef go too, personally, but this is a very poor use case for it. typedef is actually pretty rare. A struct or class is usually a better choice. > I'd like join to work with a lazy range and on chars too, allowing > code like (untested): I think that could be useful, though it's possible today already for this example (see toImpl for arrays) > There is no clean way to create a tuple on the heap, the way I have used is the best I have found. The method you used looks like the right way to do it - you just "new" it, like anything else. > I'd like Python simple functions all() and any() in Phobos2 too. > With it you are able to write code a bit less ugly like (untested): I think these would be useful too. There's already one similar thing here: http://dpldocs.info/allSatisfy But it doesn't work on the right types it looks like. If we made that a little more generic I think we'd have a winner. > My suggestion to avoid this quite bad situation is to look at sboard, it's a char[] and not a string. So a solution to this messy situation is to make string a strong typedef. This behavior is by design. http://digitalmars.com/d/2.0/phobos/std_array.html ElementType!(String)[] array(String)(String str); Convert a narrow string to an array type that fully supports random access. This is handled as a special case and always returns a dchar[], const(dchar)[], or immutable(dchar)[] depending on the constness of the input. The idea is if you ask for an array, it's because you want to do O(1) random access, which, assuming you want code points, means dchar. You probably could have done reduce!("a ~ b")("", filter....) to get the char[]... Though, I'd rethink this whole design. The board isn't a string, so you shouldn't represent it as one. I used int[9] in my code. > There are many situations where I'd like afilter() === > array(filter()) and amap() == array(map()). But, this isn't orthogonal! > auto my_turn(char xo, TBoard sboard) { > auto options = space(); > char choice = options[uniform(0, options.length)]; > sboard[to!int(choice~"") - 1] = xo; > return choice; > } I wrote this thus: int suggestMove(int player) out(ret) { assert(ret >= 0); assert(ret < 9); assert(isAvailable(ret)); } body { return randomCover(openPositions(), Random(unpredictableSeed)).front; } You can see it is a one-liner, yet not an unreadable one. The reason is because I picked a more correct representation of the board. It makes no sense whatsoever to populate an array with its indexes, then get indexes to get a value which is then converted back into an index! I you use an index in the first place, which makes conceptual sense, you save piles of busy work. > to!int(choice) doesn't do what you think it does, it converts the choice char to its integer representation: That's actually exactly what I think it does, coming from C. I'd be ok with just making cast() be the thing to do though. I usually cast for this use case anyway (again coming from C, that's just the natural way that comes to mind). > I'd like a _very_ handy std.random.choice(), that allows to write > code like (untested): I don't see the big benefit over randomCover here, but I wouldn't veto it's inclusion (if I had such power). It's a trivial function though. > In Python both for and while loops have an optional "else" clause, that runs if the loop is not exited by a break. It is a handy thing, that allows to avoid the "unbroken" boolean I have used in D. Yes, that's somewhat useful, but not enough to warrant a new language feature. I find it is usually better to make the condition explicit, or avoid it altogether with a better design. > Because D2 refused to use a handy "in" for strings, this is awful. Isn't canFind() <http://dpldocs.info/std.algorithm.canFind> that same thing? if(str.canFind('c')) > Regarding raw_input() see: > http://d.puremagic.com/issues/show_bug.cgi?id=4716 That might be useful. I even put something like it in my GUI code. I'd say make it do a combination of writef though, so you can do more complex prompts. auto number = ask!int("Enter a number, %s", name); I think I found a bug in readf() in my implementation too! It doesn't seem to handle unexpected data very well (not even offering an error code like C and C++ do...). > There is another usage of unbroken. That's only because of the code's very, very bizarre design. There's a lot of code duplication and a really strange control flow. I don't think it helps your case at all to use such horribly awful code to make points. Half of your statements are the direct result of the source code being garbage. | |||
February 08, 2011 Re: Another Phobos2 test | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Adam Ruppe | Adam Ruppe: > My implementation > http://arsdnet.net/tictactoe.d Thank you for your answers and code. Your code is better. This kind of indentations is interesting: string positionString(int pos) in { assert(...); } out(ret) { assert(...); } body { // ... } > I don't think it helps your case at all to use such horribly > awful code to make points. Half of your statements are the direct result of > the source code being garbage. The original code was not mine, and I know it's bad code. That's why I have suggested you to not work on it. I have used it just because it contains several compact constructs used in Python. My next posts I'll use a different strategy to explain things. > source: 138 lines, 2420 bytes > You can see the byte count is comparable to the python 2, but I have > more lines. Replacing the tabs with spaces, as in the original Python and D versions, and using Windows newlines, it becomes 3278 bytes. > I usually prefer "Introduction to Programming" style code than "functional code golf" style, so you see that too. Your code is not functional-style, it's procedural and contains some mutables. My discussion was about improving Phobos things that are especially useful if you write code in functional style. Programs in ML-derived functional languages are often written in a more compact style. This doesn't make them significantly more bug-prone because purity, immutability, very strong type systems, and some other of their features avoid many bugs. > I also put in a lot of contracts just because i can. I'm trying to get into the habit of that. Very good, I do the same. That code was not production code, it was an experiment focused on showing some possible improvements for Phobos. Adding contracts (and annotations as pure, const, immutable), and several other things to the code was just a way to distract the people that read that code from the purposes of the discussion. D2 shows that there are three (or more) "sizes" of functional programming: - Micro scale: when you use a line of code that uses array(map(filter())). - Medium scale: when you use normal procedural code inside small functions/methods, but the small functions are mostly pure and use mostly const/immutable data. - Large scale: when your functions are mostly procedural and sometimes use mutable inputs too, but the main fluxes of data in your large application are managed in a immutable/functional way (like from the DBMS, etc). Large-scale functional programming is done in Java programs too, and it's not much influenced by Phobos, it's a matter of how your very large program is structured. Medium-scale functional programming is well doable in D2 thanks to pure annotations and transitive const/immutable. So a question is how much micro-scale functional programming is right/acceptable in a very little or very large D2 program. I don't have an answer yet (probably the answer is: not too much). Some Scala programmers use lot of micro-scale functional style (see some little examples here: http://aperiodic.net/phil/scala/s-99/ ), but Scala allows to write that code with a significantly less noisy syntax. What I am able to say is that currently using a micro-scale functional programming style in D2 is awkward, there's too much syntax noise, making the code not so readable and maintenable. But that tictactoe code was an experiment, you need to perform some experiments, because they reveal you new things. In production code, in larger programs written in a mostly procedural language, I usually write code more similar to yours, it's just better if you need to maintain lot of code for years. In a more functional language I use a different style, but I avoid golfing if the code needs to be used for a lot of time. In script-like programs I sometimes use a little more compact style, but not as compact as the original Python code. In such Python/D scripts I don't write stuff like this: string toString() { string lineSeparator = "-+-+-\n"; string row(int start) { return format("%s|%s|%s\n", positionString(start + 0), positionString(start + 1), positionString(start + 2)); } string ret; ret ~= row(0); ret ~= lineSeparator; ret ~= row(3); ret ~= lineSeparator; ret ~= row(6); return ret; } Using what Python offers functional-style allows to write code about equally safe. Too many lines of code require more time to be written (experiments have shown that programmer tend to write approximately the same number of lines / hour. This doesn't hold up to code golfing, of course). This style of writing code is seen as "bloated" and Java-style by lot of people (by me too). This is why Java programs are sometimes 10+ times longer than Python ones, I prefer more compact Python code. On the other hand too much compact code gives problems. So as usual in programming you need to find a balance (the original tic-tac-toe program was not balanced, it was more like an experiment). > End user instructions have no place as documentation comments. Being able to print out documentation comments at runtime is almost useless - doing so indicates a high probability of bad comments. I have seen several Python programs print out documentation comments at the beginning, they avoid you to write them two times in the program. When the command-line program starts (or when a help is required by the user) the program shows a help, that's also the docstring of the whole module. It's handy and useful. > dmd -X to generate it, then dmd -J to load it up for use at compile time. Documentation comments are already included in the json output. I know, but I was suggesting something different, to turn the JSON creation into some kind of Phobos library that you may call at compile-time from normal D code. Then a compile-time JSON reader in Phobos will allow to perform certain kinds of static introspection, that later will be quite useful to create user-defined @annotations. > I wrote: struct Board { } > > If you want a separate type, make a separate type. I'm sad to see > typedef go too, personally, but this is a very poor use case for it. > typedef is actually pretty rare. A struct or class is usually > a better choice. One of my main usages of typedef was to give a specific type to arrays, as shown in that code. In this newsgroups I have shown some other usages of mine of typedef. You are able to avoid using most usages of typedef if you write code in object oriented style. > The method you used looks like the right way to do it - you just "new" it, like anything else. With "new Tuple!()()" you need to manually specify the types of all the fields, while "tuple()" spares this need, but allocates on the stack. A newTuple() function allows to do both, but I don't know how much often it is needed. >> My suggestion to avoid this quite bad situation is to look at sboard, it's a char[] and not a string. So a solution to this messy situation is to make string a strong typedef. > > This behavior is by design. I know, and I've suggested a possible alternative design, using a strong typedef to allow some higher order functions to tell apart an array of chars from a string of chars. > The idea is if you ask for an array, it's because you want to do O(1) random access, which, assuming you want code points, means dchar. What I'd like is a way to tell the type system that I am creating a sequence of 8-bit chars, and not a string of chars. If I have an array of single chars, and the type system has a way to understand this, then converting them to dchars is stupid. While if I have a string of chars, then converting their chars into dchars when I iterate on them is acceptable. > The board isn't a string, Right, it's an array of 8 bit chars. This was my point. >> There are many situations where I'd like afilter() === >> array(filter()) and amap() == array(map()). > > But, this isn't orthogonal! I know, but programming life is not orthogonal :-) Sometimes other considerations too need to be taken into account. From what I have seen I need array() often enough in D2, I can't keep lazy ranges around as often as I do in Python. One of the problems of using functional-style code in D is the syntax noise. A amap() allows to avoid two parentheses of array(map()), avoiding some noise. > You can see it is a one-liner, yet not an unreadable one. The reason is because I picked a more correct representation of the board. But your code doesn't allow to show why a choice() is useful, so you have failed :o) > It makes no sense whatsoever to populate an array with its indexes, then get indexes to get a value which is then converted back into an index! > > I you use an index in the first place, which makes conceptual sense, you save piles of busy work. I agree. > That's actually exactly what I think it does, coming from C. I'd be ok with just making cast() be the thing to do though. I usually cast for this use case anyway (again coming from C, that's just the natural way that comes to mind). Coming from Python it's not so natural (in Python single-char strings are used to represent single chars: "foo"[0][0][0][0] == "f"). I think to!int('1') == 1 is useful, but I am not sure if C-derived programmers accept/like to!int to work differtly from cast(int) in this case. >> I'd like a _very_ handy std.random.choice(), that allows to write >> code like (untested): > > I don't see the big benefit over randomCover here, but I wouldn't veto it's inclusion (if I had such power). It's a trivial function though. Right, it's trivial, but it's used often (as sorted()), and it allows to give a name to a purpose: give me one random element from a random-access sequence (a choice() may be specialized for the associative arrays too). > Yes, that's somewhat useful, but not enough to warrant a new language feature. Python designers (and Knuth too, I remember) have had a different opinion. I find it useful. > I find it is usually better to make the condition explicit, or avoid it altogether with a better design. Using "else" for loops is usually a good enough design. For Python programmers it's clear and I don't remember one problem caused by it. But of course there are ways to avoid it, if you don't want or you can't use it. > I'd say make it do a combination of writef though, so you can do more complex prompts. > > auto number = ask!int("Enter a number, %s", name); This comment looks good for that little enhancement request of mine on Bugzilla :-) > I think I found a bug in readf() in my implementation too! It doesn't > seem to handle unexpected data very well (not even offering an error > code like C and C++ do...). Curiously I have found no bugs in both D and Phobos while I have translated that Python2 code. I think it's the first time it happens for a so long proggy. This means that the D debugging efforts are giving their first results!! :-) Eventually most 100 lines long D2 programs will usually find no new bugs. Bye, bearophile | |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply