I have a JPanel and a vector of JButtons and want to add each button to the panel.
The issue I'm having is that I have a variable btns that represents the vector of buttons, but the macro function just sees it as a symbol, not a vector. Is there a way to expand it somehow?
Here's the code:
(defmacro add_all [panel btns]
;; help?
)
(let [x 10, y 10
btns get_buttons] ;; just a vector of JButton objects
(doto (JPanel.)
(.setLayout (GridLayout. x y))
(add-all btns)))
I would like this to expand to:
(doto (JPanel.)
(.setLayout (GridLayout. x y))
(.add (btns 0))
(.add (btns 1))
;; etc
(.add (btns 99)))
There's no need for a macro (and indeed the problem as stated cannot be solved with a macro). Just write a function which operates on a panel and a seq of buttons:
(defn add-all [panel buttons]
(doseq [button buttons]
(.add panel button)))
Related
I have adapted this method from p.216 Joy of Clojure.
(defn makeButton [txt func panel]
(let [btn (JButton. txt) ] ;bfun (symbol func) ]
(doto btn
(.addActionListener
(proxy [ActionListener] [] (actionPerformed [_]
(func))) ;(bfun)))
))
(.add panel btn)
))
Plus this test code:
(defn fba [] (prn "fba"))
(defn fbb [] (prn "fbb"))
(def funs [fba fbb])
(def btnames ["B1" "B2"])
Buttons & actionPerformed are created thusly in a gui setup function:
(doseq [i (range (count btnames)) ]
(makeButton (get btnames i) (nth funs i) aJpanel))
This works as I want; clicking "B1" prints string "fba", etc. My difficulty arises when I want to attach to 'actionPerformed' something such as: ns1.ns2/myFunction instead of these simple cases (i.e. use a list/vector of several namespaced qualified functions). The exception reported is
wrong number of args (0) passed to Symbol
From the 'makeButton' function above my attempt to solve this is commented out. What is an idiomatic clojure solution to this?
This code works for me in the REPL. I'm using clojure.pprint/pprint as an example of a function that is in a different namespace:
(import '[javax.swing JFrame JPanel JButton]
'[java.awt.event ActionListener])
(def panel (JPanel.))
(def frame (doto (JFrame. "Hello Frame")
(.setContentPane panel)
(.setSize 200 200)
(.setVisible true)))
(defn make-button [label f panel]
(let [button (JButton. label)]
(doto button
(.addActionListener
(proxy [ActionListener] []
(actionPerformed [evt]
(f evt)))))
(.add panel button)))
(make-button "One" clojure.pprint/pprint panel)
(.revalidate frame)
If I have a function that evaluates to a function
(defn func1 [c1 c2]
(fn [x1 x2]
...do some stuff with c1 c2 x1))
that I use elsewhere in a map or reduce, is it better to use inline
(defn func2 [x y z]
(reduce (func1 x y) z (range 20)))
or to let bind it first
(defn func2 [x y z]
(let [ffunc (func1 x y)]
(reduce ffunc z (range 20))))
In the first case I would be worried that a new function over x and y is generated each step through the reduce.
The evaluation of the function call (func1 x y) is done once in each case.
The rule for evaluating a function call in Clojure consists of evaluating all the expressions that are provided as its arguments and then invoking the function with those values.
If you define the following higher order function:
(defn plus []
(println "calling plus")
+)
And then call reduce in the following way:
(reduce (plus) [0 1 2 3])
A single calling plus is printed, showing the function plus is invoked only once.
The same thing happens when using the let form:
(let [f (plus)]
(reduce f [0 1 2 3]))
Hope it helps.
I'm trying to figure why this particular function isn't working as expected. I suspect from the error message that it has something to do with the way I'm creating the empty vector for the accumulator.
I have a simple function that returns a sequence of 2-element vectors:
(defn zip-with-index
"Returns a sequence in which each element is of the
form [i c] where i is the index of the element and c
is the element at that index."
[coll]
(map-indexed (fn [i c] [i c]) coll))
That works fine. The problem comes when I try to use it in another function
(defn indexes-satisfying
"Returns a vector containing all indexes of coll that satisfy
the predicate p."
[p coll]
(defn accum-if-satisfies [acc zipped]
(let [idx (first zipped)
elem (second zipped)]
(if (p elem)
(conj acc idx)
(acc))))
(reduce accum-if-satisfies (vector) (zip-with-index coll)))
It compiles, but when I attempt to use it I get an error:
user=> (indexes-satisfying (partial > 3) [1 3 5 7])
ArityException Wrong number of args (0) passed to: PersistentVector
clojure.lang.AFn.throwArity (AFn.java:437)
I can't figure out what's going wrong here. Also if there is a more 'Clojure-like' way of doing what I'm trying to do, I'm interested in hearing about that also.
The problem is probably on the else clause of accum-if-satisfies, should be just acc not (acc).
You could use filter and then map instead of reduce. Like that:
(map #(first %)
(filter #(p (second %))
(zip-with-index coll)))
You could also call map-indexed with vector instead of (fn [i c] [i c]).
The whole code would look like that:
(defn indexes-satisfying
[p coll]
(map #(first %)
(filter #(p (second %))
(map-indexed vector coll))))
As for a more Clojure-like way, you could use
(defn indexes-satisfying [pred coll]
(filterv #(pred (nth coll %))
(range (count coll))))
Use filter instead of filterv to return a lazy seq rather than a vector.
Also, you should not use defn to define inner functions; it will instead define a global function in the namespace where the inner function is defined and have subtle side effects besides that. Use letfn instead:
(defn outer [& args]
(letfn [(inner [& inner-args] ...)]
(inner ...)))
One more way to do it would be:
(defn indexes-satisfying [p coll]
(keep-indexed #(if (p %2) % nil) coll))
I am using ClojureScript to detect which browser-specific version of 'requestAnimationFrame' method is defined. I use the following code:
(defn animationFrameMethod []
(let [window (dom/getWindow)
options (list #(.-requestAnimationFrame window)
#(.-webkitRequestAnimationFrame window)
#(.-mozRequestAnimationFrame window)
#(.-oRequestAnimationFrame window)
#(.-msRequestAnimationFrame window))]
((fn [[current & remaining]]
(cond
(nil? current) #((.-setTimeout window) % (/ 1000 30))
(fn? (current)) (current)
:else (recur remaining)))
options)))
This works fine, and it's not terrible, but I would really like to be able to put the method names in a list, i.e.
'(requestAnimationFrame webkitRequestAnimationFrame ...)
And then call a macro for each symbol in the list to generate the anonymous function code.
I would like something to work like so:
user> (def name webkitRequestAnimationFrame)
user> (macroexpand '(macros/anim-method name window))
#(.-webkitRequestAnimationFrame window)
But I played around with macros for a while, and was unable to achieve this effect. Part of the problem is that method names and the dot notation work strangely, and I'm not even sure if this is possible.
Any tips to get this working? Thanks!
Remember that javascript objects are also associative hashes, so something like this should work without resorting to macros (untested)....
(def method-names ["requestAnimationFrame"
"webkitRequestAnimationFrame"
"mozRequestAnimationFrame"
"oRequestAnimationFrame"
"msRequestAnimationFrame"])
(defn animationFrameMethod []
(let [window (dom/getWindow)
options (map (fn [n] #(aget window n)) method-names)]
((fn [[current & remaining]]
(cond
(nil? current) #((.-setTimeout window) % (/ 1000 30))
(fn? (current)) (current)
:else (recur remaining)))
options)))
I'd like my Clojure program to exit when a JFrame is closed.
I'm attempting to trap and handle the close event as such:
(def exit-action (proxy [WindowAdapter] []
(windowClosing [event] (fn [] (System/exit 0)))
)
)
(.addWindowListener frame exit-action)
This doesn't throw any obvious errors but it also doesn't appear to do what I want.
Assistance is appreciated.
Answer:
Adapting Rekin's answer did the trick:
(.setDefaultCloseOperation frame JFrame/EXIT_ON_CLOSE)
Note that that is:
setDefaultCloseOperation
not:
setDefaultOperationOnClose
In Java it's:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
More elaborate examples can be found in official Java Swing tutorial about Frames
I would use EXIT_ON_CLOSE, but the reason your first attempt didn't work is that the body of proxy should contain (System/exit 0), not (fn [] (System/exit 0)). Rather than exiting, you were returning (and then throwing away) a function that, when called, would exit.
Here's a short demonstration program I showed on my blog a while ago
(ns net.dneclark.JFrameAndTimerDemo
(:import (javax.swing JLabel JButton JPanel JFrame Timer))
(:gen-class))
(defn timer-action [label counter]
(proxy 1 []
(actionPerformed
[e]
(.setText label (str "Counter: " (swap! counter inc))))))
(defn timer-fn []
(let [counter (atom 0)
label (JLabel. "Counter: 0")
timer (Timer. 1000 (timer-action label counter))
panel (doto (JPanel.)
(.add label))]
(.start timer)
(doto (JFrame. "Timer App")
(.setContentPane panel)
(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
(.setLocation 300 300)
(.setSize 200 200)
(.setVisible true)))
(println "exit timer-fn"))
(defn -main []
(timer-fn))
Note the line in timer-fn[] that sets the default close operation. Just about like Java but with a little syntax fiddling.
The purpose of the blog entry was to show an example of a closure in Clojure.