Re: Seeking computer-programming job (Sunnyvale, CA)
Kaz Kylheku wrote:
On 2009-05-15, Seamus MacRae <smacrae319@live.ca.nospam> wrote:
eric-and-jane-smith wrote:
Seamus MacRae <smacrae319@live.ca.nospam> wrote in
news:guhr3h$ffm$1@news.motzarella.org:
If it merely transforms the list, and doesn't care whether they're
runnable, it should not matter. Suppose it sorts the list by priority.
But what if it does care? What if it takes a list of a million objects,
and deletes 17 of them from that list, because those 17 aren't runnable?
In Common Lisp, deleting 17 objects from a list of a million can be done
quickly and efficiently. But you seem to be implying you have to copy the
other 999983 objects to a list of runnables, because otherwise they would
be in a list of objects, even though they're all runnable.
Not in Java. In Java, you can use:
/**
* Removes non-<code>Runnable</code> objects from <code>list</code>
* and returns pruned list as a <code>List<Runnable></code>;
* caller is responsible for synchronizing the list if necessary.
*/
@SuppressWarnings("unchecked")
/*
* Justification: the only unchecked cast is in the return
* line, and all non-Runnables have been removed from the
* list at that time. The returned list is a list of
* Runnables unless a data race occurred. Correct behavior,
* as ever, depends on the caller synchronizing where
* appropriate.
*/
public List<Runnable> pruneList (List<?> list) {
for (Iterator<?> i = list.iterator(); i.hasNext();) {
// Old "for" used intentionally
Object o = i.next();
if (!(o instanceof Runnable)) i.remove();
}
return (List<Runnable>)list;
}
Untested.
The above vomit
Fascinating. An illogical, emotional outburst in response to ordinary
computer code.
maens:
;; By default, all things are assumed non-runnable.
(defmethod is-runnable (obj) nil)
;; But things subclassed from runnable, well, are.
(defmethod is-runnable ((obj runnable-class)) t)
;; Function call to remove non-runnables from a some-list, returning
;; a new list with only runnables in it:
(remove-if-not 'is-runnable some-list)
REMOVE-IF-NOT is a functional construct which leaves the old list alone
(but the new list may share substructure with the old). If you
want to modify the original list, use DELETE-IF-NOT.
(setf some-list (delete-if-not 'is-runnable some-list))
Obviously correct by inspection.
Whoops. There are at least two things missing from your version:
1. Type-safety. There's nothing to identify the output list as
containing only runnables. Therefore there's nothing to identify
the input list to someplace else as containing only runnables. So
the compiler won't catch it if the wrong list gets used there.
2. Documentation. About half my code was documentation. The first
comment would be processed by the Java tool-chain to produce
an HTML file documenting the method, its behavior, and the
conditions under which it was guaranteed to do the right thing.
Your code has no such documentation, and indeed you didn't
even define a callable function prune-list or whatever, let
alone make it so that anyone could right click on the word
"prune-list" in some code subsequently and get a concise little
pop-up telling them exactly what it did and what its preconditions
were. Certainly I haven't a clue how thread-safe your
implementation is just from looking at the code.
Your remove-if-not version sounds like it might be thread-safe, because
it sounds like it behaves like my second (and not-quoted-by-you) code
sample, which copies the list. Someone else expressed concern about the
time spent copying objects (though in Java, at least, you'd be copying
pointers rather than whole objects).
Your delete-if-not version sounds like it probably is not thread-safe.
Is there a way to associate a monitor with the list and lock it in lisp?
There is in Java, and it's very simple:
synchronized (list) {
runnableList = pruneList(list);
for (Runnable r : runnableList) {
r.run();
}
}
This removes the non-runnables and runs the runnables in sequence, while
holding a monitor associated with the list.
As an aside, your version seems to be prescriptive rather than
descriptive -- def statements that execute rather than declarations of
classes and methods. If so, this points to a different problem: what
happens if there are circular dependencies? In Java, if two classes or
other chunks of code need to refer to one another they can. In Lisp, it
sounds like an imperative interpreter runs down a list of def-foo
commands to actually build the program structure. If it executes the
statement that creates the foo-bar function first, and this references
the baz-quux function, the latter will still be undefined.
This can also happen in C but C lets you declare a function prototype
without specifying its implementation, or name a struct or similarly, so
you can subsequently reference it; you would ordinarily prototype foobar
and bazquux together in a header and then provide their implementations
in a .c file, which would include that header; the foobar implementation
can then refer to the bazquux one without any problems. C++ is similar,
though you can also implement them as inlines in the header:
int bazquux(string);
inline bool foobar(string s) { return bazquux(s) == 0; }
inline int bazquux(string s) {
int r = get_length(s);
if (r == 0) return 0;
return r + foobar(get_prefix(s))?1:0;
}
(with get_length and get_prefix presumed defined somewhere else, and
get_prefix(s) always shorter than s).
Java is even smarter: you can just have methods, classes, and suchlike
refer to each other, click build in your IDE, and off you go.
Java also acknowledges that the "shift" key has other uses than just
with the 9 and 0 keys. :)