Arc Forumnew | comments | leaders | submit | cchooper's commentslogin

By the sound of things, the problem is that the variables created by one function are being modified by other functions. In that case, the solution is to use local variables instead of global variables. To do that, you just need to wrap the function body in a single let form:

  (def ugh (str v)
    (let x 15
      (for i 0 (- (len str) 1) (set xi (* x 2))
        (if (is "A" (cut str i (+ i 1)))  
            (set x (+ xi v))
            (set x (- xi v)) i)) x))
The sets will refer to the locally created variable, not a global variable.

  (= x 100000)
  => 100000
  (ugh "AAAA" 1)
  => 255
  x
  => 100000
The global x has not been modified, because the function created a local x variable and used that instead.

Here's a more concise version of that function:

  (def ugh (str v)
    (let x 15
      (on c str 
          (= x (* x 2))
          (if (is #\A c) (++ x v) 
              (-- x v)))
      x))
and here's a version that uses no sets at all:

  (def ugh (str v (o x 15))
    (let x2 (* 2 x)
      (if (is "" str) x
          (is #\A str.0) (ugh (cut str 1) v (+ x2 v))
          (ugh (cut str 1) v (- x2 v)))))

-----

1 point by thaddeus 6006 days ago | link

ah! I see. my rudimentary tests for (= x(...)) and probably having x set from previous iterations of code led me to believe (= x(...)) always changed the global x.

Thanks to all! T.

-----

1 point by cchooper 6017 days ago | link | parent | on: Plop in Arc

It looks interesting, but it has a dependency on Maxima, which is a huge library. Can you tell which features it uses?

-----

2 points by skenney26 6017 days ago | link

I dropped Moshe a line to see what he has to say regarding Plop's dependency on Maxima.

I've looked through the first few files and so far have only encountered fairly straight-forward common lisp. Hopefully whatever Maxima features are used will be relatively easy to implement.

-----

1 point by skenney26 6017 days ago | link

Coincidently, Moshe just finished updating Plop so that it runs correctly without Maxima.

-----

1 point by cchooper 6017 days ago | link | parent | on: Arc Challenge

This is my final attempt. It's faster, shorter and easier to read than my first attempt, but the performance is still shockingly bad.

  (= *nwords* (table))

  (w/infile f "c:/big.txt" (whilet l (readline f) (counts (tokens (downcase l) whitec) *nwords*)))

  (def edits1 (word)
    (with (alphabet "abcdefghijklmnopqrstuvwxyz"
           n (len word))
      (dedup:accum add
        (forlen i word (add:+ (cut word 0 i) (cut word (+ 1 i)))
                       (each c alphabet (add:copy word i c)))
        (for i 0 (- n 2) (add:copy word i (word (+ 1 i)) (+ 1 i) word.i))
        (for i 0 n (each c alphabet (add:string (cut word 0 i) c (cut word i)))))))

  (def known-edits2 (word edits) 
    (accum add (each e edits (map add (known (edits1 e))))))

  (def known (words) (keep [*nwords* _] words))

  (def correct (word)
    (let edits (edits1 word)
      (best (compare > [*nwords* _]) (or (known:list word) (known edits) (known-edits2 word edits)))))

-----

4 points by cchooper 6024 days ago | link | parent | on: Arc Challenge

This is my first attempt:

  (= *nwords* (counts:tokens (downcase:w/infile f "big.txt" (tostring (drain:pr:readline f))) whitec))

  (def edits1 (word)
    (with (alphabet "abcdefghijklmnopqrstuvwxyz"
           n (len word))
      (dedup:accum add
        (forlen i word (add:+ (cut word 0 i) (cut word (+ 1 i))))
        (for i 0 (- n 2) (add:string (cut word 0 i) (word (+ 1 i)) (word i) (cut word (+ 2 i))))
        (forlen i word (each c alphabet (add:string (cut word 0 i) c (cut word (+ 1 i)))))
        (for i 0 n (each c alphabet (add:string (cut word 0 i) c (cut word i)))))))

  (def known-edits2 (word) (flat:map known:edits1 (edits1 word)))

  (def known (words) (keep [*nwords* _] words))

  (def correct (word)
    (let candidates (or (known:list word) (known:edits1 word) (known-edits2 word))
      (best (compare > [*nwords* _]) candidates)))
This achieves nothing close to 10 words per second in the worst case scenario, more like one word per minute.

-----

2 points by lojic 6024 days ago | link

Thanks! I'd say that definitely compares favorably with the Clojure version aesthetically.

The performance is a little disappointing though :( Here's the performance of my Ruby (i.e. slowest of all languages!) version:

  ~/sync/code/ruby$ irb
  irb(main):001:0> load 'spelling_corrector.rb'
  => true
  irb(main):002:0> def time_sc n
  irb(main):003:1> t1 = Time.now
  irb(main):004:1> n.times { correct "zaqxsw" }
  irb(main):005:1> puts Time.now - t1
  irb(main):006:1> end
  => nil
  irb(main):007:0> time_sc 10
  25.809097
So that's 2.6s per word.

The Clojure version is considerably faster (0.33s per word):

  user=> (time (dotimes i 10 (correct "zaqxsw" *nwords*)))
  "Elapsed time: 3254.863 msecs"
For words for which a correction was found, the Clojure version processed 900/s, Ruby 100/s

-----

1 point by cchooper 6024 days ago | link

Oddly, the Ruby version runs more slowly every time I run it. I think memory locality may be the problem, as the Ruby process grows each time (now at 85 MB). That's nothing compared to my MzScheme process which is now consuming 700MB. That's probably due to the inefficient way in which nwords is constructed. A bit of tuning there might work wonders for overall performance.

-----

1 point by babo 6019 days ago | link

Please post python's speed as a reference, it's wicked fast as far as I remember.

-----

1 point by cchooper 6024 days ago | link

This change reduces the running time by about a third:

  (def known-edits2 (word) 
    (accum add (each e (edits1 word) (map add (keep [*nwords* _] (edits1 word))))))

-----

4 points by rkts 6023 days ago | link

Probably because it's incorrect. You should have (edits1 e) at the end there.

-----

2 points by cchooper 6022 days ago | link

True. The correct version is still faster though.

-----

2 points by cchooper 6029 days ago | link | parent | on: Syntax highlighting, an unsolved problem

I've just discovered hl-sexp-mode and I'm finding it quite good.

-----

3 points by andreyf 6028 days ago | link

For the curious: http://fuhm.net/Screenshot.png

-----

5 points by cchooper 6031 days ago | link | parent | on: Brainstorm: syntax sugar for lambdas

As rkts said, if you start specifying names of variables, then you aren't really saving many characters. The other problem with some of the suggestions is that you don't get the benefit of omitting the outer parens of an expression:

  [car _]
Note that there are no parens in that function. On the other hand, most suggestions about putting the variables at the start reintroduce the parens:

  [x y || (list x y)]
It would be better to do this:

  [x y || list x y]
One option is to do what K does: parameters are called x, y and z by default:

[list x y z]

It's concise, but not really much different to [list _1 _2 _3] or Clojure's #(list %1 %2 %3).

I like the idea of re-purposing \ as a synonym for 'fn', as in some other languages. Personally, I've never used \ as a single-escape character in any Lisp code I've written, and all instances can be replaced with |...| anyway. But this is still not as concise as [...]

  (\(x) (car x))
Perhaps using \ as an infix operator would be better:

  (x)\(car x)
Pushing it further:

  x\(car x)

  x\y\z\(list x y z)
This means you can't use rest arguments with this notation, but then I almost never use rest arguments with anonymous functions, just as I don't use implicit progn (which you can't represent with [...] either). You would have to resort to 'fn' in these situations, which is fine because the language should be optimised for the most common case rather than the general case.

One final observation: almost all Arc functions take either one, two or three arguments, with the last one often being a rest argument. So these are the cases that need to be optimised.

-----

3 points by andreyf 6030 days ago | link

almost all Arc functions take either one, two or three arguments, with the last one often being a rest argument. So these are the cases that need to be optimized

Although the other analysis is insightful, IMO, this is the most important part of your post. Although, I'm not looking to optimize all functions, just those with bodies smaller than about twice the size of the "fn (argument list)" code.

-----


1. It depends what you mean by 'temporarily'? To change it in a local scope, you can shadow the function variable like this:

  (def foo (x y) (pr x y))

  (let foo (fn (y x) (foo x y)) (foo "one" "two"))
  => twoone
2. Square-bracket notation is the most concise way to partially apply a function to all but one argument:

  (map [foo _ "bar"] '(1 2 3))
  => 1bar2bar3bar
Currying is a different thing to partial application and isn't supported by Arc, but it is possible to write a curry operator that can produce the curried equivalent of any function. (Note that the 'curry' operator defined by pg in ANSI Common Lisp does not actually curry the function, strictly speaking). Such an operator would have to take an argument saying how many arguments the original function had, as Arc has no way of reflecting on functions at the moment.

3. map is currently implemented so that extra list members are simply ignored:

  (map + '(1 2 3) '(1 2))
  => (2 4)
It would be quite easy to define new versions of map that do what you require. For example, repeating the last element:

  (def repeat-map (f . ls)
    (if (pos no ls) nil
        (all ~cdr ls) (list:apply f (map car ls))
        (cons (apply f (map car ls)) 
              (apply repeat-map f (map [if (~cdr _) _ (cdr _)] ls)))))

-----

2 points by shader 6033 days ago | link

Thanks, that was what I was looking for. Unfortunately, I think I've configured something wrong in git or ssh, and cannot clone the arc repo for actually using it ;)

By temporarily, I originally meant without defining a shadow function, like you did, just reorganizing them for the single call. But that might not be easy to do in a concise, flexible way so I suppose shadow functions make the most sense.

It seems awfully quiet around here.

-----

2 points by cchooper 6033 days ago | link

You shouldn't need ssh just to clone the repo. This should work fine:

git clone git://github.com/nex3/arc.git

However, you can't push using that URL, so you'll end up with an un-pushable copy. Have you tried looking through all the guides? http://github.com/guides/home

To reorder arguments in a single call... it would certainly be easy to do that for binary functions:

  (def swap (f)
    (fn (x y) (y x)))

  ((swap foo) "foo" "bar")
  => barfoo
That's about the best I can do,

As for the silence, it appears almkglor and stefano are busy working on hl (http://github.com/AmkG/hl/tree/master). I don't know if they still read the forum. I suspect a lot of other people are getting disheartened.

Also, until there's another release of Arc, there isn't much to talk about.

-----

1 point by shader 6032 days ago | link

Yes, I think I've followed all of the guides. However, I keep getting the "Permission (publickey)" error. I wonder if there are more configurations like username or email address that have to be the same as what github expects?

-----

2 points by cchooper 6033 days ago | link

And here's a function that performs true currying on a function:

  (def curry (f n (o args nil))
    (if (is 0 n) (apply f (rev args))
        (fn (x) (curry f (- n 1) (cons x args)))))
An example of usage:

  (subst "bar" "foo" "catfood")
  => "catbard"

  (= curried-subst (curry subst 3))

  (((curried-subst "bar") "foo") "catfood")
  => "catbard"

-----

1 point by shader 6033 days ago | link

What if you wanted to curry the function in a different order? :)

I suppose you would just curry a shadow function? Or could a utility/macro be made to easily map one order of parameters to another?

-----

1 point by cchooper 6033 days ago | link

The problem is: what's a concise way of expressing the new order? Perhaps something like this:

  ((reorder foo (x y z) (z x y)) 1 2 3)
but it's so clunky it's not really worth it. If you could reflect on functions to see their parameter list, then you could do this:

  (def foo (x y z) ...stuff...)

  ((reorder foo (z x y)) 1 2 3)
A little better.

-----

2 points by Darmani 6033 days ago | link

May give you a few ideas: http://en.wikipedia.org/wiki/Permutation_group

-----

1 point by cchooper 6032 days ago | link

Ah yes, cycle notation!

  ; convert a cycle into transpositions
  (def trans (cycle (o first nil))
    (if (no cycle) nil
        (~cdr cycle) (list:list cycle.0 first)
        (cons (list cycle.0 cycle.1)
              (trans (cdr cycle) 
                     (if first first cycle.0)))))

  ; permute a list using a list of disjoint cycles
  (def permute (cycles l)
    (with (ts (apply join (map trans cycles))
           ret (copy l))
      (map [= (ret (- _.1 1)) (l (- _.0 1))] ts)
      ret))

  (permute '((1 2 3) (4 5)) '(a b c d e))
  => (c a b e d)

-----

2 points by shader 6032 days ago | link

hmm. I think that cycle notation is sometimes shorter than just stating the final positions, but only rarely.

How about a function that does:

  >(reorder '(2 5 4 1 3) '(a b c d e))
  '(b e d a c)
That makes more sense in many cases. But having a function that does permutations using cycle notation is probably also useful.

-----

2 points by cchooper 6042 days ago | link | parent | on: Programming Language Design Process

Isn't this just the difference between a language and it's implementation?

-----


Only if you ignore symbols and numbers. Lisp symbols are not LL(1), e.g. 123456A is a valid symbol.

-----

7 points by cchooper 6043 days ago | link | parent | on: Programming Language Design Process

I also have no problem with the way pg is doing things. However, the flack he's getting is starting to reach epic proportions. Perhaps the following quote from Kent Pitman at Lisp50 explains it: "Soliciting volunteers gives critics something to do, which dilutes their passion, pacifies them, and makes them feel involved." Conversely, if they feel they have nothing to do, or their work isn't appreciated, they become critics.

I'm not sure the use of an open forum has been much of a success so far. When pg first announced the existence of Arc, he asked people to email him with suggestions. People were supposed to suggest features from other languages and then give examples of how that would make code shorter. Eventually, he posted all the replies on his web site, and although there were lots of ideas, not a single person had actually done what was asked. This forum has been better, but far from perfect. For example, has anyone has actually tried to write a fast profiler yet?

I think that if you want to offer a language to the community, and expect useful work out of it, you need to provide a structure that people can work in. For example, Perl 6 started with an 'RFC phase' and then moved on to 'Apocalypses'. Writing an RFC forces you to put a lot more thought into your ideas than you would if you were just posting to a forum. Writing the Apocalypses made sure that there was plenty of feedback on these ideas. People had something to do and knew where they stood.

So I don't think it really matters whether you release a compiler. What matters is whether people have a structure they can work in. That's why everyone wants to write libraries for Arc. Writing a library is something that one person can accomplish. It's a structure that channels people's energies towards a given end-point, and they are rewarded through people using the library in their own projects.

For Arc, the compiler was both a help and a burden. It helped because people could actually try out ideas. It was a burden because everyone started working on the compiler, not the language.

So I think community-aided design can work, but it needs to be harnessed correctly. The most important ingredient is probably high-quality feedback on what people are doing. Of course, that also requires the most effort, but no one ever said it was easy. :)

-----

7 points by almkglor 6043 days ago | link

> For example, has anyone has actually tried to write a fast profiler yet?

http://arclanguage.com/item?id=5318

-----

1 point by cchooper 6043 days ago | link

Oops! :)

-----

More