Arc Forumnew | comments | leaders | submit | Darmani's commentslogin
6 points by Darmani 6054 days ago | link | parent | on: Return value of pr and prn

Actually, especially since Arc lacks a debugger, I find it very convenient to be able to just place a pr: or pr. in front of some symbol to get an expression printed out without the behavior changing.

-----

1 point by bOR_ 6054 days ago | link

Wouldn't it be even more beneficial to return the whole, rather than just the first (so that it prints out the whole content of the symbol)?

-----

2 points by gus_massa 6054 days ago | link

I agree. I think that (pr 1) should return 1.

The problem is with (pr 1 2). Should it return 1 or 2?

-----

1 point by stefano 6053 days ago | link

It could return the list (1 2), but this would mean to cons up a new list every time we use 'pr just to throw it away. Multiple return values would solve the problem. Why not add them to the language?

-----

1 point by bOR_ 6053 days ago | link

at least in agent-based models, printing is not the thing that is happening so often that consing up a new list is prohibitive.

How much more expensive is cons compared to multiple return values? I remember there being a long discussion against mrvs.

-----

2 points by stefano 6052 days ago | link

> How much more expensive is cons compared to multiple return values?

It depends on the GC used and on the implementation. mrvs are usually pushed on the stack, so there is no GC overhead. When I develop applications with Lisp, I notice that most of the time reducing the amount of garbage speeds up the program noticeably. It's not only a problem of consing, it's also a problem of collecting the garbage. More garabage, more collection cycles.

-----

1 point by xrchz 6043 days ago | link

do pr or prn create a string? it might be less expensive to return that string if it is created

-----

3 points by Darmani 6058 days ago | link | parent | on: Reepeeaat

rep is taken.

  arc>def
  #3<tagged mac #<procedure>>
  arc>(rep def)
  #<procedure>

-----

5 points by Darmani 6067 days ago | link | parent | on: Ask PG: Arc on V8?

Arc implementation in JavaScript: http://halogen.note.amherst.edu/~jdtang/arclite/

-----


Shame I didn't notice before the edit limit expired, but this post got screwed up by the formatting system. The first first line should read "Arc's (errr....MzScheme's) * :". The example in the last paragraph should read "that .* gets translated to (#<eof> *)". And everything in between should not be italicized...

(http://news.ycombinator.com/formatdoc should really be a lot easier to find.)

-----


Well, since I voted for the basic library with sin, cos, pi, gcd, etc, I felt obligated to implement a basic math library with sin, cos, pi, gcd, closely related fucntions, and everything I needed or thought I'd need to implement those.

Implementations guaranteed not to be optimally efficient:

Edit: Fixed a mistake in bringing out-of-period numbers into period in sin and cos

  (= pi 3.141592653589793238)
  (= 2*pi (* 2 pi))
  
  (def signum (n)
    (if (> n 0)
        1
      (< n 0)
        -1
        0))
  
  (def floor (x)
    (if (< x 0)
      (- (trunc x) 1)
      (trunc x)))
  
  (def ceil (x)
    (if (< x 0)
      (trunc x)
      (+ (trunc x) 1)))
  
  (defmemo fac (n)
    (if (is n 0)
      1
      (* n (fac (- n 1)))))
  
  ;Existing mod only accepts integers. Left intact as "modulo"
  ;This mod copies the behavior of Ruby's mod
  (def mod (dividend divisor)
    "Returns the remainder from dividing dividend by divisor.
    Adds divisor once more if they are of different signs so that the result is always of the same sign as divisor."
    (if (is divisor 0)
          (error "modulo undefined for 0")
        (isnt (signum dividend) (signum divisor))
          (+ divisor (- dividend (* divisor (trunc (/ dividend divisor)))))
          (- dividend (* divisor (trunc (/ dividend divisor))))))
  
  (def sin (x)
    "Returns the sine of x in radians."
      (let x (let red (mod x 2*pi)
                (if (> (abs red) pi)
                  (- red (* (signum red) 2*pi))
                   red))
         ;Taylor polynomial; 0.0 is to cast to float
         (- (+ 0.0 x (/ (expt x 5) (fac 5)) (/ (expt x 9) (fac 9)))
             (/ (expt x 3) (fac 3)) (/ (expt x 7) (fac 7)))))
              
  (def cos (x)
    "Returns the cosine of x in radians."
       (let x (let red (mod x 2*pi)
                 (if (> (abs red) pi)
                   (- red (* (signum red) 2*pi))
                   red))
         ;Taylor polynomial
         (- (+ 1.0 (/ (expt x 4) (fac 4)) (/ (expt x 8) (fac 8)))
             (/ (expt x 2) (fac 2)) (/ (expt x 6) (fac 6)))))
  
  (def tan (x)
    "Returns the tangent of x in radians."
    ;Lazy definition
    (/ (sin x) (cos x)))
  
  (def int? (x)
    "Returns whether x is an integer"
    (is (mod x 1.0) 0.0))
  
  (defmemo prime (n)
    "Returns the nth prime. 2 is the 0th prime."
    (if (< n 0)
          nil
        (is n 0)
          2
          (let prev-primes (map prime (range 0 (- n 1)))
              ((afn (i)
                  (if (no (breakable:each p prev-primes ;Each always returns nil, so a break returns t
                              (if (int? (/ i p))
                                (break t))))
                        i
                        (self (+ i 1))))
                (+ 1 (last prev-primes))))))
      
  
  (def prime-factorization (n)
    "Returns a list each prime up to the greatest prime in n paired with the power of that prime in n.
    E.g.: (prime-factorization 20) returns ((2 2) (3 0) (5 1)).
    Use (reduce * (map [apply expt _] ...)) to change a prime factorization into the number."
    (rev:accum keep
      (let p-ord 0
        (while (> n 1)
            (with (p (prime p-ord)
                    pow 0)
              (until (isnt (mod n p) 0)
                (++ pow)
                (zap [/ _ p] n))
              (keep (list p pow)))
            (++ p-ord)))))
  
  (def gcd (x y)
    "Returns the greatest common divisor of x and y."
      (reduce * (map [apply expt _]
          (map (fn (a b)
                    (list (car a) (min (cadr a) (cadr b))))
            (prime-factorization x) (prime-factorization y)))))
  
  (def lcm (x y)
    "Returns the least common multiple of x and y."
    (/ (* x y) (gcd x y)))

-----

4 points by shader 6145 days ago | link

Nice. I only did the gcd function, so you've got an amazing head start on me. However, the version of gcd that I did was Euclid's algorithm. Probably faster than prime factorization; and I included a case for only one number, and a list of greater than two.

Since I couldn't commit to github for some reason, here's the code:

  ;;Int (predicate)
  (def int (n)
    "Returns true if n is an integer; i.e. has no decimal part."
    (is (trunc n) n)) ; return true if the number is the same as itself without the decimal part

  ;;Greatest Common Denominator
  (def gcd l
    "returns the greatest common denominator, or divisor, of a list of numbers. Numbers should be integers,
     or the result will default to 0."
    (with (a (car l) c (cdr l))
      (if (len> c 1) 
            (gcd a (gcd (car c) (cdr c))) ; handle lists of more than two numbers
          (no a) 0
          (no c) (abs a)
          (let b (car (flat c))
            (if (or (~int a) (~int b)) 0 ; skip non-integers
                (is a b) a ; return common divisor
                (if (> a b)
                      (gcd (- a b) b)
                    (gcd (- b a) a)))))))
Update:

Figured out what I was doing wrong with github. Math library now started in lib/math.arc. Currently it only contains my gcd function and the 'int test, but feel free to add any other math functions.

In the future, we may need to make it a math folder, with individual files for trig functions, hyperbolic functions, calculus, matrix and vector function, quaternions, etc. But for now one file is more than enough to contain my lowly gcd :)

-----

1 point by Darmani 6145 days ago | link

  arc>(def int (n)
    (is (trunc n) n))
  #<procedure: int>
  arc>(int 1.0)
  nil
My original int? was identical to yours, but that's why I changed it. Although, now that I think about it, since prime-factorization discards the results of operations that cast n to a rational, that's unnecessary. Still, there will need to be separate functions for testing if a num's type is int (which yours does), and testing if a num lacks decimal places (which mine does).

(And yes, Euclid's is definitely faster than prime factorization -- I originally looked at better algorithms for prime finding and gcd but I decided to settle for the "pure" approach -- sieve instead of some Fermat's or another fast primality test, prime factorization based instead of Euclid's.)

P.S.: Feel free to make use of the code I posted.

-----

1 point by shader 6145 days ago | link

Ah, I didn't notice that. Apparently I should test things more carefully next time :)

I don't know if you have github access, but if you do, you can feel free to add your own code to the math.arc lib. I probably won't have a chance to add it myself in the next week or so. If I can, though, I will definitely try to do so.

-----

2 points by eds 6142 days ago | link

I just pushed 'floor, 'ceil, and 'fac from http://arclanguage.org/item?id=7280, and 'sin, 'cos, and 'tan to Anarki.

In doing so, I noticed one bug in Darmani's original implementation of 'floor and 'ceil. 'floor would return incorrect results on negative integers (e.g. (floor -1) => -2), and 'ceil on positive integers (e.g. (ceil 1) => 2). This has been corrected on Anarki.

I also used mzscheme's 'sin, 'cos, and 'tan instead of Darmani's, not because of speed issues, but because of decreased precision in those functions. In order to get maximum precision it would be necessary to calculate the Taylor series an extra couple of terms, which I didn't feel like doing at the time.

I didn't commit 'signum, 'mod, 'prime, or 'prime-factorization, because I wasn't sure if they were needed except for computing 'sin, 'cos, and 'gcd... but feel free to commit them if you want.

-----

1 point by shader 6133 days ago | link

I have a few questions:

1) Isn't pushing those math functions straight from scheme sort of cheating? I mean, maybe I'm just wrong, but wouldn't the solution be more long term if we avoided scheme and implemented the math in arc?

2) Shouldn't fac be tail recursive? Or is it, and I just can't tell? Or are you just expecting that no one will try and compute that large of a factorial

3) If some one did compute that large of a factorial, is there some way for arc to handle arbitrarily sized integers?

-----

1 point by almkglor 6133 days ago | link

1) No, you should implement in the math in the underlying machine instructions, which are guaranteed to be as precise and as fast as the manufacturer can make it. The underlying machine instructions are fortunately possible to access in standard C libraries, and the standard C library functions are wrapped by mzscheme, which we then import in arc.

2) It should be, and it isn't.

  (defmemo fac (n)
    ((afn (n a)
       (if (> n 1)
           (self (- n 1) (* a n))
           a))
     n 1))
3) Yes, arc-on-mzscheme handles this automagically. arc2c does not (I think it'll overflow)

-----

3 points by kens 6133 days ago | link

Implementing numerically stable and accurate transcendental functions is rather difficult. If you're going down that road, please don't just use Taylor series, but look up good algorithms that others have implemented. One source is http://developer.intel.com/technology/itj/q41999/pdf/transen...

That said, I don't see much value in re-implementing math libraries in Arc, given that Arc is almost certainly going to be running on a platform that already has good native math libraries.

-----

1 point by shader 6133 days ago | link

I figured that being close to machine instructions was a good thing, but I thought that we should do that via some other method, not necessarily scheme, which may or may not remain the base of arc in the future.

That being said, if you think that pulling from scheme is a good idea, why don't we just pull all of the other math functions from there as well?

-----

2 points by almkglor 6133 days ago | link

> That being said, if you think that pulling from scheme is a good idea, why don't we just pull all of the other math functions from there as well?

Yes. Yes it is. http://arclanguage.com/item?id=7288

That's what I said ^^

-----

1 point by shader 6133 days ago | link

Ok, I added that tail optimized version to math.arc.

Do you want to have a separate math libs for the scheme functions and native implementations? You already suggested the possibility.

-----

2 points by almkglor 6132 days ago | link

Err, "native implementations" being?

Actually I think it might be better if we had a spec which says "A Good Arc Implementation (TM) includes the following functions when you (require "lib/math.arc"): ...." Then the programmer doesn't even have to care about "scheme functions" or "java functions" or "c functions" or "machine language functions" or "SKI functions" - the implementation imports it by whatever means it wants.

Maybe also spec that the implementation can reserve the plain '$ for implementation-specific stuff.

-----

4 points by almkglor 6145 days ago | link

Interesting, you actually defined it mathematically ^^

It might also be useful to create a lib/mz-fast-math.arc which just imports 'sin, 'cos, 'tan from mzscheme using '$

-----

1 point by jmatt 6143 days ago | link

I think that's a good idea.

-----

2 points by Darmani 6174 days ago | link | parent | on: (delist somelist)

A month or two ago, I wrote Sokoban in Arc, and produced a small library for multidimensional lists in the process. This function should interest you:

  ;dims needed because ((abc)) may be a two-dimensional list containing abc or 
  ;a one-dimensional list containing (abc)
  (def muldim-map (dims f lst)
    ((afn (lst)
      (if (no lst)
        nil
        (if (is dims 0)
          (f lst)
          (cons (muldim-map (- dims 1) f (car lst)) (self (cdr lst))))))
      lst))

-----

1 point by bOR_ 6174 days ago | link

If I read it right, it is a function that for example, can grab the nth entry from a cell (or nth row / column from a matrix)?

You have the code out there somewhere? It is probably good to have some more examples of Arc out in the open, and there'll be things in it I can learn from.

-----

1 point by Darmani 6174 days ago | link

Sorry, I misunderstood your query last night.I just realized the function actually doesn't apply in this specific instance here.

It's a version of the normal map generalized to n-dimensional lists; I forgot when writing it that normal map accepts multiple lists.

However, with that added, it would be useful if you, say, had (((1 2) (3 4)) ((5 6) (7 8))) and wanted ((6 8) (10 12)); that would end up being (apply muldim-map 2 + (list (list (list 1 2) (list 3 4)) (list (list 5 6) (list 7 8))))

(I was going to download Arc's source code and use the source of the original map as a guide to writing a version of muldim-map that could handle multiple multi-dimensional lists on the spot, but I don't believe these high school computers have software that can handle tarballs.)

-----

2 points by Darmani 6174 days ago | link

However, I just realized I had another function in that library that could perform the task you described, although it would be overkill. It's called something like move-muldim-block; it takes a multi-dimensional list and returns a new multi-dimensional list with a multi-dimensional piece of that list moved elsewhere, using a combining function to determing the value of the area in which it was moved (I used it in Sokoban to simplify code that would let let me move a crate character onto a storage character and change it to the crate-on-storate character).

In other words, if lst is set to ((1 2) (3 4)) and f is some function, (move-muldim block (list 1 0) (list 1 2) (list 0 0) +) would be equivalent to (((+ 1 3) (+ 2 4)) ((f 3) (f 4))) (it moves a 1x2 block from coordinates (1 0) to coordinates (0 0)). Just take the car of the result and you have what you're looking for (in which case, we could omit the last argument and use the default value).

For this specific task, apply is a heck of a lot simpler, but I can still post the code if you're interested. I personally found it very interesting to realize that this could be done, and that muldim-move-block is a little more useful than I first thought.

-----

1 point by bOR_ 6174 days ago | link

Hmm. Right now I store the positions of my mobile piec elsewhere than in the world-list. Your function might be handy later on. I haven't yet decided if I want every actor of the world stored in the world (even if some of them might overlap), or store the mobile actors in a separate list.

Right now the thing about sokoban implementations that would interest me most is if you've done anything fancy to generate the output. Having an update of a 2d world scroll by ever so often makes it hard to track movement of a single character through this world. Did you work on that?

-----

3 points by almkglor 6174 days ago | link

Since Arc is a language targeted for the web... have you considered the possibility of generating a web page for it, with maybe next >> and << prev links?

-----

1 point by bOR_ 6174 days ago | link

That might actually work, and just autorefresh the page every second or so :)

First priorities now are finishing the wandering algorithm for the animals so that they're no longer steered but steer themselves, and generalize the code I've some more so that adding more behaviours than just biting things is easy. I'll add the visuals-through webpage idea to that list :)

-----

1 point by Darmani 6174 days ago | link

Sorry, not really. I just used a simple function to print out a 2-dimensional list and resized my terminal window so that the grid would be in the same place after every sequence of moves, which gives the illusion that the same grid is being redrawn.

-----

1 point by Darmani 6174 days ago | link

Small mistake -- the invocation would be (move-muldim block (list 1 0) (list 1 2) (list 0 0) + f) (I hope no-one thought I'd be silly enough to set the function to find the replacement-fn by looking up the global value of f...).

-----


If we had first class special forms, one might have some success with something like this:

  (= *call-stack* (list))

  (let old-fn fn
    (mac fn (args . body)
      (w/uniq (name ret)
        `(,old-fn ,args
            (push (list ,name ,body) *call-stack*)
             (let ,ret (do ,@body)
              (pop  *call-stack*)
               ,ret)))))
From there, just hack the REPL to include an on-error around everything. Perhaps some more complex hacking could be done to insert code that would allow one to track where in a function one before an error occurs.

Since we don't have first-class special forms, in the meantime, one can just replace this redefinition of fn with a similarly-named macro and then replace all invocations of fn to this macro. Should probably wrap this around the primitives too.

Of course, I'm not advocating this as ideal; I'm just exploring the possibility of doing this in pure Arc (or almost pure; I forget whether the REPL is in Arc or Scheme). Aside from seed issues, I'm sure this setup has many problems. Is it possible to get this kind of thing to work with multiple threads?

-----

3 points by almkglor 6185 days ago | link

For threads: Anarki has thread-local variables:

  (= call-stack* (thread-local))
  (= (call-stack*) (list))

  (let old-fn fn
    (mac fn (args . body)
      (w/uniq (name ret)
        `(,old-fn ,args
            (push (list ,name ,body) (call-stack*))
             (let ,ret (do ,@body)
              (pop  (call-stack*))
               ,ret)))))
Note the extra layer of parenthesization.

-----


  (= range cons)

  (def within (range num)
    (and (>= num (car range)) (<= num (cdr range))))

  (def directawayfrombounds (creature)
    (let awaydir (fn (pos dim dir)
       (if (within (range 0 (- dim 1)) (+ pos dir))
         dir
         (- dir)))
      (list 
        (awaydir (car creature!pos) (len (car world)) (car creature!dir))
        (awaydir (cadr creature!pos) (len (car world)) (cadr creature!dir)))))

-----

1 point by almkglor 6192 days ago | link

'range is an arc.arc function in canonical Arc, so you should be leery of redefining it - if you decide to use some random library, and it uses 'range in the canonical sense, you can expect it to break.

Instead, define it in subfunction form:

  (def directawayfrombound (creature)
    (withs (range
              cons
            within
              (fn (range num)
                (let (min . max) range
                  (<= min num max)))
            awaydir
              (fn (pos dim dir)
                (if (within (range 0 (- dim 1)) (+ pos dir))
                    dir
                    (- dir)))
            (xpos ypos) creature!pos
            (xdir ydir) creature!dir)
      (list
        (awaydir xpos (len world.0) xdir)
        (awaydir ypos (len world.0) ydir))))

-----

1 point by Darmani 6191 days ago | link

Thanks for the correction. Although I did know about the canonical range function, it slipped my mind when I wrote that (though I did have the feeling that there was a "real" range function). I should have mentioned that I wasn't near any computer with Arc when I wrote, though I still should have known better.

-----

1 point by bOR_ 6191 days ago | link

Both of you, thanks!, more learning arc. (1) "withs" is "with sequentially"', so that "range" makes sense in the function "awaydir". (2) cons isn't just appending a number to a list.

I like the table!key syntax, not sure what I'm thinking about world.0 as a syntax, especially because world.0.0 doesn't give what I'd expect (first element of the first element of world), but just returns the same as world.0

  (def makeworld (x)
     (= world (n-of x (n-of x nil))) ; world of nil
     nil)

-----

1 point by Darmani 6191 days ago | link

Actually, almkglor's code would (I'm pretty sure) have worked with just a normal with; function definitions are unevaluated at definition, so that (fn (x) (afergergghersgergferg x)) would not give "Error: afergergghersgergferg is not a function" until it's called. If a defined a function called afergergghersgergferg in the meantime, as with a normal with statement, it should have worked.

Anyway, you're not alone in being surprised that x.y.z expands to (x y z) instead of (x (y z)).

-----

2 points by absz 6191 days ago | link

Not quite. It works in the toplevel because doing (def name args ...) is effectively to (= name (fn args ...)); this establishes a global, not a lexical, binding. Using let/with/withs adds a lexical binding, and since functions (or "closures") close over their scopes, they can't get at functions defined at the same time. Thus, you need to use withs. Observe:

  arc> (with (foo (fn (x) (* 2 x)) bar (fn (y) (foo y)))
         (prn (foo 10))
         (prn (bar 10))
         t)
  20
  Error: "reference to undefined identifier: __foo"
  arc> (withs (foo (fn (x) (* 2 x)) bar (fn (y) (foo y)))
         (prn (foo 10))
         (prn (bar 10))
         t)
  20
  20
  t

-----

1 point by bOR_ 6190 days ago | link

That implies that at the top level, the order in which I define my functions (which use each other) doesn't matter..

nice! Then I can group them in a way that is more sensible for humans.

Anyway, the program is now far enough that I've a critter running around the field and filling its memory with associations between the things it encounters. Next stop is to let it learn what is edible and to let it steer itself based on its observations :).

-----

3 points by Darmani 6237 days ago | link | parent | on: Yet another attempt at an Arc logo

I really like this logo. It is minimalist, elegant, and abstract, like Arc.

-----

7 points by Darmani 6238 days ago | link | parent | on: Defining Setable Forms

  (let olddef def
    (mac def (name args . body)
      (if (acons name)
        `(def= ,(cadr name) ,args ,@body)
        `(,olddef ,name ,args ,@body))))
Hope that fits what you're looking for.

(I also hope that works -- I'm not at home and can't test it.)

-----

3 points by eds 6238 days ago | link

It almost works... but unfortunately because we don't yet have first class macros it fails with:

  Error: "Bad object in expression #3(tagged mac #<procedure>)"

-----

2 points by absz 6238 days ago | link

Try removing the comma in front of olddef; it should work then.

-----

4 points by Darmani 6237 days ago | link

Unfortunately, that won't work because the form is being returned to a place outside the closure where olddef was defined; when eval is given the form containing olddef, it will not recognize it.

Here's a better definition:

  (let olddef def
    (mac def (name args . body)
      (if (acons name)
        `(def= ,(cadr name) ,args ,@body)
        (apply (rep olddef) (cons name (cons args body))))))

-----

2 points by absz 6237 days ago | link

Shouldn't that be (mac def ...)?

Anyway, nice work, but damn do we need 1st-class macros.

-----

3 points by Darmani 6237 days ago | link

Oops -- thank you. Fixed.

-----

More