Arc Forumnew | comments | leaders | submit | akkartik's commentslogin
3 points by akkartik 3420 days ago | link | parent | on: Passing values up the line

Ah, you're right. I think jsgrahamus might be expecting result to be modified by this line:

  (let (diagonals result row) (get-next-row board-size diagonals result)
    ..)
But it just creates a new shadowing binding for result, which exists only for the lifetime of the let, and is lost once the let is finished. Think of let as pushing a new value for its variable(s) on a stack before running its body, then popping the new values off, leaving behind any preexisting values.

-----

2 points by jsgrahamus 3420 days ago | link

Thanks, I'm familiar with that practice. I'm also used to passing by reference so that changes made to the variable in the newer function get passed back to the calling function.

-----

2 points by akkartik 3420 days ago | link

Yeah, makes sense. However, let always creates a new binding and never modifies existing bindings. That's why it makes you indent its body to the right. Compare:

  (= x 3 y 4)
  (+ x y)
with:

  (let (x y) '(3 4)
    (+ x y))
The indentation is a hint that these are new x and y variables, compared to any earlier x and y variables.

Besides let there are indeed functions in Arc where you can pass lists or tables by reference (though not primitives like numbers or characters). However, it's usually a good idea to try to avoid these, again so you can practice a less imperative style of programming (http://arclanguage.org/item?id=19709). My usual approach is to first build a program with promiscuous copying everywhere, and then if it turns out to be too slow pick the 2% of cases that can speed it up a lot and make them destructive/call-by-reference, because the cost of doing so is that it makes the program harder to understand.

-----

2 points by jsgrahamus 3419 days ago | link

Thanks, makes sense.

-----

2 points by akkartik 3421 days ago | link | parent | on: Passing values up the line

Can you show the other functions like get-next-row? Thanks.

-----

2 points by jsgrahamus 3420 days ago | link

Here you go:

  (def get-next-row (board-size diagonals result total)
       (with (column (len result) finished nil new-diagonals nil row (car (rev result)))
         (while (and (~ finished) (~ (> (++ row) board-size)))
	        (if (~ (mem row result))
	  	  (do
		    (= new-diagonals (calculate-diagonals board-size column row))
		    (if (and (~ (mem (car new-diagonals) diagonals))
			     (~ (mem (car (rev new-diagonals)) diagonals)))
		      (do
		        (= finished t)
		        (= result (rev (cons row (cdr (rev result)))))
		        (for i 1 2 (push (pop new-diagonals) diagonals)))))))
         (list diagonals result (if finished row nil) total)))

-----

2 points by akkartik 3420 days ago | link

There's still other functions that are missing, such as goto-next-column, goto-previous-column, etc.

Anyways, I think you've gotten me to work on this problem as well :) I found it super hard when I solved it in C back in '97. Perhaps I should go back and see if I've gotten any better at programming.

-----

2 points by jsgrahamus 3420 days ago | link

Here is the whole program. I still need to check for reflections [if I already have (2 4 1 3), do not allow (3 1 4 2)].

As I said, I'm using this to learn arc. So if y'all have any suggestions, I'm all ears :-)

  ;; nqueens - Solve n queens problem
  ;;
  (def calculate-diagonals (board-size column row)
       (let new-diagonals nil
         (if (is row 1)
  	   (push column new-diagonals)
  	   (if (is column 1)
  	     (push row new-diagonals)
  	     (push (+ column row -1) new-diagonals)))
         (if (is row 1)
  	   (push (- column board-size 1) new-diagonals)
  	   (if (is column 1)
  	     (push (- 1 board-size row) new-diagonals)
  	       (push (- column board-size row) new-diagonals)))
         new-diagonals))
  
  (def get-next-row (board-size diagonals result total)
       (with (column (len result) finished nil new-diagonals nil row (car (rev result)))
         (while (and (~ finished) (~ (> (++ row) board-size)))
  	      (if (~ (mem row result))
  		(do
  		  (= new-diagonals (calculate-diagonals board-size column row))
  		  (if (and (~ (mem (car new-diagonals) diagonals))
  			   (~ (mem (car (rev new-diagonals)) diagonals)))
  		    (do
  		      (= finished t)
  		      (= result (rev (cons row (cdr (rev result)))))
  		      (for i 1 2 (push (pop new-diagonals) diagonals)))))))
         (list diagonals result (if finished row nil) total)))
  
  (def goto-previous-column (diagonals result)
       (list (cdr (cdr diagonals)) (rev (cdr (rev result)))))
  
  (def goto-next-column (result)
     (rev (cons 0 (rev result))))
  
  (def print-result (diagonals result total)
       (prn result)
       (let (diagonals result) (goto-previous-column diagonals result)
         (list (cdr (cdr diagonals)) result (++ total))))
  
  (def nqueens (board-size)
    (with (diagonals nil finished nil jsg 0 result '(0) row nil total 0)
  	(while (~ finished)
  	       (let (diagonals2 result2 row2 total2)
  		 (get-next-row board-size diagonals result total)
  		 (= diagonals diagonals2 result result2 row row2 total total2)
  		 (if row
  		    (if (is (len result) board-size)
                      (do
  			(let (diagonals2 result2 total2)
  			  (print-result diagonals2 result2 total2)
  			  (= diagonals diagonals2 result result2 total total2)))
  		      (= result (goto-next-column result)))
  		    (if (is (len result) 1)
  		      (= finished t)
  		      (let (diagonals2 result2) 
  		        (goto-previous-column diagonals result)
  			(= diagonals diagonals2 result result2))))))
  	(prn "Number of results: " total))
    nil)

-----

2 points by zck 3419 days ago | link

> I still need to check for reflections [if I already have (2 4 1 3), do not allow (3 1 4 2)].

There's a cool technique I read about to solve problems like this. I'll put it a few lines down in case you want to think about it yourself.

.

.

.

Sort them, then check for equality! I learned this when reading about anagrams.

-----

3 points by jsgrahamus 3419 days ago | link

So if two results were (2 4 1 3) and (3 1 2 4), both of them sort to (1 2 3 4) so delete one of them? Of course all of the results will sort to (1 2 3 4) for a 4x4 board.

Not sure I understand.

-----

2 points by zck 3419 days ago | link

Oh, you're right. I misunderstood what you meant by "reflections".

-----

2 points by akkartik 3420 days ago | link

Thanks! I think I'll try to solve it on my own without looking at yours in depth first. That might take me a few days. But feel free to keep asking questions in the meantime.

-----

2 points by jsgrahamus 3420 days ago | link

Thanks.

-----

2 points by jsgrahamus 3420 days ago | link

This took me too many hours in my work language to solve. Initially I next just translated it to arc, but that didn't seem to work, so I started again in arc from scratch. I sure learned a lot about arc and debugging in it.

It took me a while to wrap my mind around this problem. Sure is different than CRUD work, reports and interfaces.

-----

1 point by akkartik 3428 days ago | link | parent | on: ASK: Emacs inferior-arc on Windows

Are you able to get inferior-lisp working on Windows? Maybe the appropriate forum could help with any gotchas there?

If you do figure this out don't forget to come back and tell us!

-----

2 points by akkartik 3431 days ago | link | parent | on: Multiple Return Values

  (let (latitude longitude)  (func1 street-number street city state)
     (make-map latitude longitude)
     (func2 latitude)
     (func3 longitude))

-----

3 points by akkartik 3431 days ago | link | parent | on: Multiple Return Values

The most elegant approach is to keep your func1 unchanged and call it like this:

  (let (latitude longitude) (func1 a b c)
    ..)
In general you want to try to avoid = in Lisp..

-----

2 points by jsgrahamus 3431 days ago | link

Thanks. So, how do you get the 2 results from func1 into latitude and longitude?

-----

2 points by akkartik 3431 days ago | link

My code snippet is doing that. The let is doing a sort of pattern matching and binding latitude to the first element of the result, and longitude to the second.

-----

2 points by zck 3431 days ago | link

I'll explain a little further, because Arc's let is unusual. Unlike most Lisps, the variable-value pairs are not themselves enclosed in parentheses.

In this code:

    (let (latitude longitude) (func1 a b c)
We're not binding latitude to the value of longitude, but binding that whole thing to the return value of (func1 a b c).

A simpler example:

    (let (name score) '("Steve Wiebe" 1064500)
      (prn name " was the first person to score over a million points in Donkey Kong, with a score of " score))
This prints:

    Steve Wiebe was the first person to score over a million points in Donkey Kong, with a score of 1064500

-----

3 points by jsgrahamus 3431 days ago | link

And, of course, the "regular" form of arc has no parentheses:

  arc> (let hat 5 (prn "hat = " hat))
  hat = 5
  "hat = "
  arc>

-----

2 points by jsgrahamus 3431 days ago | link

I thought that let looked odd, because I thought that with a let in arc, you had 1 variable/value pair. Thanks, zck, for pointing out the difference. And thanks, akkartik, for repeatedly telling me this.

-----

2 points by waterhouse 3428 days ago | link

The corresponding macro in Common Lisp has an 18-character name.

  ; SBCL
  * (destructuring-bind (x y) '(1 2) (+ x y))
  3
  ; Arc
  arc> (let (x y) '(1 2) (+ x y))
  3

-----

2 points by jsgrahamus 3431 days ago | link

Why avoid = in lisp/arc?

-----

1 point by akkartik 3431 days ago | link

As long as you use '=' it's easy to keep programming C or MUMPS in Lisp. Avoiding '=' forces you to give up old habits and become fluent with (writing as well as reading) deeply nested calls, 'let' and recursion. Those are some big reasons to use Lisp rather than imperative languages. So if you don't use them you're missing out.

-----

2 points by jsgrahamus 3431 days ago | link

Makes sense. Thanks.

It really does force me to think a different way. Painful sometimes, too.

-----

2 points by akkartik 3433 days ago | link | parent | on: Odd errors

You need parens around the variable bindings in with, otherwise Arc can't tell where bindings end and the body begins.

  (with (columns-rows  -1
         list1  (rev rows-tried)
         list2  '())
    ..)

-----

1 point by akkartik 3433 days ago | link | parent | on: Odd errors

Ah, thanks for spotting that so quickly. Arc warns when we replace a function with another. Perhaps it should also do so when we replace a function with anything else?

Edit 28 minutes later: this is now done.

  arc> (= alist t)
  *** redefining alist
  t
https://github.com/arclanguage/anarki/commit/b7d17d8c13

Let me know if y'all run into any problems. Unit tests pass, but we can always add more..

-----

2 points by jsgrahamus 3433 days ago | link

Will anarki run on Windows?

-----

1 point by akkartik 3433 days ago | link

I did see it running momentarily on Windows a few weeks ago: http://arclanguage.org/item?id=19458. It might have a few issues, but if you report them I'll try to fix them.

-----


Discoverability is a perennial issue. Arc 3.1 is pretty comprehensively documented at https://arclanguage.github.io/ref (even if it's a lot to read). n-of is in the page on list operations: http://arclanguage.github.io/ref/list.html Unfortunately changes to Arc 3.1 in Anarki are harder to find; nobody's gotten around to building a copy of Ken Shirriff's reference for Anarki. One good place to the first order is the https://github.com/arclanguage/anarki/tree/master/CHANGES directory on Github.

-----

3 points by akkartik 3434 days ago | link | parent | on: Unit-test.arc 1.0 incoming

Hey, one last-minute suggestion: suite-w/setup is a pretty long and ugly name. How about if we just always include a set of setup bindings in suite, even if they're empty? It would look nice and consistent with def and mac:

  ; without setup
  (suite foo ()
    (test must-bar
      (assert-same 2 (+ 1 1)))

  ; with setup
  (suite foo (a 1
              b 2)
    (test must-bar
      (assert-same b (+ a a)))
I wouldn't be too disappointed if you decide against this, partly since it would save me the trouble of fixing my translator all over again :)

-----

2 points by zck 3433 days ago | link

Interesting. I like the consistency. I don't love how it makes the most common case (I think the no-setup case is most common) and adds more code to it.

Maybe I can come up with a simpler, less awful thing.

Perhaps:

    (suite foo (setup a 1
                      b 2)
           (test must-bar
                 (assert-same b (+ a a))))
It does seem relatively simple. Hrm.

-----

2 points by akkartik 3433 days ago | link

I'm kinda growing to like my idea the more I think about it. You're right that it adds 3 characters to the common case, but lisp has a long tradition of empty parens in various places. Saving characters shouldn't be a high priority, IMO. Paul Graham's notion of conciseness counts tokens, not characters.

But yeah, happy to see what other ideas we can come up with. I think the setup keyword above is worse; lisps don't tend to have keywords that aren't functions or macros. Then again, test is already a keyword that's not a function.. Hmm, I like it better if you indent it like this:

  (suite foo
         (setup a 1
                b 2)
         (test must-bar
               (assert-same b (+ a a))))
The benefit of this approach is that it makes the syntax seem extensible. It's obvious how new features should be added.

Ok, I could live with this :)

-----


I'm surprised by the redefinition warnings. They don't happen for me. Perhaps you have some of your own code being loaded? Or maybe you have an old version? (Though I don't remember make-list ever being a thing.)

-----

2 points by jsgrahamus 3434 days ago | link

I'm caught. Still using the old version of arc 3.1 on Windows 7 with racket.

-----

More