views:

118

answers:

3

I am looking to traverse a NxN area, given the starting points X,Y and the size of the square to traverse. E.g. given X=10,Y=12,Size=2 - i want to generate 10,10 ; 10,11 ; 11,10 and 11,11.

I came up with this, but it seems to go on and on infinitely:

traverse({X,Y,Xend,Yend}) ->

    % print X,Y values here....     

    case (X == Xend-1) andalso (Y == Yend-1)  of
     true -> 
        ok;
     _->   
        case (Y < Yend-1) of
           true ->
      traverse({X,Y+1,Xend,Yend});
           _->
      traverse({X+1,Y,Xend,Yend})
        end
    end.

I called the above function from another function using:

Size = 3,
traverse({10,20,10+Size,20+Size}).

What am I doing wrong? I am actually new to the functional paradigm, and I tried implementing this on C, using "return" in the place of "ok" above, and it worked, but I am guessing, i'm not thinking "functionally" enough!

+2  A: 

Try this:

traverse({X,Y,Xend,Yend}) ->
 dotraverse(X, Y, Xend, Yend, _StartX=X).

dotraverse({Xend,Yend,Xend,Yend}, SX) ->
 ok;

dotraverse({X,Y,Xend,Yend}, SX) when X<Xend ->
  %print
  dotraverse({X+1, Y, Xend, Yend}, SX);

dotraverse({Xend,Y,Xend,Yend}) ->
 dotraverse(SX,Y+1, Xend, Yend}, SX).

Note: untested but you get the gist of it.

jldupont
`dotraverse({X,Y,Xend,Yend}, SX) when X==Xend, Y==Yend` is more pattern-matchy written as `dotraverse({Xend,Yend,Xend,Yend}, SX)`
Zed
@jldupont: same stands for last clause ;)
Zed
Thanks, I had fun implementing in both yours and Zed's methods, but in the end, I went for Zed's method, thanks a lot nevertheless.
jeffreyveon
+3  A: 
traverse(X0, Y0, S) ->
    [
     io:format("coords: ~p~n", [{X,Y}])
    || X <- lists:seq(X0, X0 + S - 1),
       Y <- lists:seq(Y0, Y0 + S - 1)
    ],
    ok

in shell:

1> traverse(10, 12, 2).
coords: {10,12}
coords: {10,13}
coords: {11,12}
coords: {11,13}
ok
Zed
Using map() you can do it in-place : lists:map(fun({X,Y}) -> io:format("Traversing ~p,~p~n", [X,Y]) end, [ {X, Y} || X <- lists:seq(StartX,StartX+Side), Y <- lists:seq(StartY,StartY+Side) ]) The X and Y in the function being mapped are different from those in the list comprehension. I did that to illustrate name space. It might be easier to follow, and possibly a better coding style anyway, if we use different names : lists:map(fun({T1,T2}) -> io:format("Traversing ~p,~p~n", [T1,T2]) end, [ {X, Y} || X <- lists:seq(StartX,StartX+Side), Y <- lists:seq(StartY,StartY+Side) ])
Tim
@Tim, a disadvantage of this way is that you build the whole list of {X,Y} coordinates. But I don't see any advantage over using a list comprehension.
Zed
... then question was: "Why doesn't my method work"... so technically, you didn't answer the queston ;-)
jldupont
Did it in the comments :)
Zed
Although you can interpret my answer as "Your method doesn't work because you did not do it this way" :-)
Zed
The OP clearly states he wants to traverse a square. Even the example has a single Size variable. Two variables would clearly be redundant.
Zed
@zed: in any case, I admire your agility with `list comprehensions`. Until next time, have fun :-)
jldupont
@zed, yes, I was just trying to illustrate that you don't have to write a stand-alone function in order to use the list comprehension, but otherwise basically the same answer as yours (hence i did it as a comment and not an answer). space didn't allow me to say everything i wanted to.
Tim
@Tim, OK I see. But you can also use list comprehensions in-place, not just lists:map().
Zed
@zed, yes, totally agree. I'm not being at all clear about where I was going. I should have written something like the below, to illustrate that you can completely decouple the traversal from what function is applied each time.6> Fun1 = fun({X,Y}) -> io:format("Traversing ~p,~p~n", [X,Y]) end.#Fun<erl_eval.6.49591080>7> lists:map(Fun1, [ {X, Y} || X <- lists:seq(StartX,StartX+Side), Y <- lists:seq(StartY,StartY+Side) ]).Traversing 10,20Traversing 10,21...BTW I see from your profile you're working in telecom. Using Erlang there ?
Tim
Yep. At Ericsson. But I am only a "consumer" of Erlang :-)
Zed
Awesome :) List comprehension is fast becoming quite a handy thing to me!
jeffreyveon
+2  A: 

This is a good question coming from someone new to the functional paradigm. The larger mistake here is the way the code is thought out, not the fact that it doesn't work.

Since you are coming from C, it's natural that you're writing the code in an imperative style, because that's how you're thinking.

In the functional style you have to tell the compiler "what" you want to do, not "how to do it" like you do in imperative languages such as C. The example Zed gives using the list comprehension is the right way.

Writing code is the best way to learn, but make sure you also focus on thinking in a functional manner. It takes a while to make the leap, but keep writing code and you'll get there eventually.

If you're serious about Erlang, read either Joe Armstrong's book or that of Cesarini and Thompson, or read the "OTP Design Principles User's Guide" from erlang.org. These will help you start to think differently, and the time spent will be well worth it.

Tim