Hey everyone, I am trying spit the output of a distinct
call which returns a lazy sequence. Thing is, it writes clojure.lang.LazySeq@4e9d9e
in the text file instead of a list that I want. How do I force the lazy sequence to be computed so that I can write it into a file?
it depends on how you are outputting the sequence to the text file as you could use doall
or see the answer here which uses pr-str
on the sequence
https://stackoverflow.com/questions/23405852/clojure-printing-lazy-sequence
you could also call vec
on it
what would make a call chain like this clean? (resembling some->
with a custom predicate?)
(let [context (call1 context)]
(if (continue? context)
(let [context (call2 context)]
(if (continue? context)
(call3 context)
context))
context))
cond->
That was my first thought too, but I don't think it'll work quite right if you want to keep calling (continue? context)
on the updated value of context
(assuming context
is an immutable value).
yes, not sure how to make cond->
work with the βnewβ context
values
Oh that's right, the predicates are for the original input (I always assume they get threaded as well). I suppose you could repeat the actual code as part of the predicates but it wouldn't be pretty...
The new context is the result of the last computation. In the case below, the context after would be 2 and false? Would be the continue. (cond-> 1 ; we start with 1 true inc ; the condition is true so (inc 1) => 2 false (* 42) ; the condition is false so the operation is skipped (= 2 2) ( 3)) ; (= 2 2) is true so ( 2 3) => 6 ;;=> 6
So it would work right? But it looks like it also could just be a recursive call.
(loop [c context] (if (c? (Recure (call c)) c)
Or something, I'm on my phone
Are the calls different?
Personally, I would probably do something like
(let [context (call1 context)
context (if (continue? context) (call2 context) context)
context (if (continue? context) (call3 context) context)
...
]
context)
just because I know future me will be able to read that code and know exactly whatβs going on.Itβs a bit boiler-plate-y, but you avoid nesting x levels deep, and the repetition makes it obvious that itβs just repeated iterations of βcall a fn and check for continue?β
yes, this let
looks better, making the pattern obvious
> Are the calls different? yes, they are, just working on the same/similar shape
Well the repeating part is the specific predicate and the call so (Loop [calls [a b c] context] (let [call (first calls)] (if (and call (C? Context)) (recure (rest calls) (call context) context) or something, lisp isn't easy to your type on a phone.
Actually, use reduce
So you don't have to check to calls are gone
I would avoid reduce and recursion for something like this. I think I'd rather do it like @manutter51 said or even with as->
:
(as-> (call1 context) context
(if (continue? context) (call2 context) context)
(if (continue? context) (call3 context) context)
...)
you meant something like this with reduce?
(reduce (fn [context f]
(let [new-context (f context)]
(if (continue? new-context)
new-context
(reduced new-context))))
context
[call1 call2 call3])
yea
this is what i wrote as an example:
(reduce
(fn [n f]
(if (int? n)
(f n)
(reduced n)))
0
[inc inc str inc]);; => "2"
why avoid reduce? @pavlos
It's harder to read, you have to put some effort to understand what's going on.
Smarter is not always better. π
In that sense, my solution using as->
is also less readable than just using a let
and if
s
maybe. How hard something is to read is rather subjective.
When i look at the let binding solution i have to scan each word twice to see if things are reused, and whats really changing, so to me, that's harder to read.
if you want to handle the elses
then this looks like Railway Oriented Programming
fun fact: the entire lazy sequence is realized in order to calculate the hash for the sequence that's part of the string, it's just that the toString method for clojure.lang.LazySeq doesn't show the sequence contents doall is irrelevant here
(cmd)user=> (def s (distinct (map #(doto % println) [1 1 1 2 3 2 3 4 5 4 3 2])))
#'user/s
(cmd)user=> (str s)
1
1
1
2
3
2
3
4
5
4
3
2
"clojure.lang.LazySeq@1c3e4a2"
(cmd)user=> (doall s)
(1 2 3 4 5)
(cmd)user=> (str s)
"clojure.lang.LazySeq@1c3e4a2"
(cmd)user=> (pr-str s)
"(1 2 3 4 5)"
@pavlos reduce is only harder to read if you aren't used to it, IMHO it's worth getting used to
it's simpler to me than deep nesting of conditionals and here it conveys two important pieces of information that ease the reading of the code: there's a series of inputs that are always handled the same way, and any one of them could be a short circuit condition
i didnt find reduce easier to read either, but thats why im asking in beginners.. it might make sense to actually def and name the paramterized reduce function - but what would be a nice name for that?
something like stop-when-not-int
(or some more succinct version of that) could work
I find that the patterns of reduce functions are so predictable I rarely need names for clarity, it's always a function of two parameters, the first arg is always the init or return value of the last call, the second arg is always an item from the collection being traversed
those properties might seem arbitrary but they never change so you only need to figure them out once
this came to me multiple times, thats why i thought there might be an idiom for this (or a threading where some->
is a special case like (def some-> (partial something-> nil?))
but that doesn't account for the repetition of applying some function to a previous value, then halting if a condition is met
;; and reduce does capture that pattern easily
(reduce (fn [state f]
(let [next-state (f state)]
(if (pred? next-state)
next-state
(reduced state))))
initial-state
functions)
core.async question:
Why does this fail (hangs indefinitely in repl):
(<!! (go (>! (chan) (+ 41 1))))
But this works:
(let [c (chan)] (<!! (go >! c (+ 41 1))))
(Update: nope, left off a paren!)
In other words, making a new channel inside a go block seems to be a no-no, the channel has toΒ be made outside the go block even though the go block returns its channel...
Thanks for any answers, mostly just trying to wrap my head around this conceptually.
in the second one you are not calling >!
, it's simply referenced and discarded
the corrected version hangs just like you initial example did:
user=> (let [c (chan)] (<!! (go (>! c (+ 41 1)))))
...
oof thanks. paren error sorry. now to figure out why it's hanging....
because the go block is blocked waiting for someone to consume from the channel
an unbuffered channel is a synchronous exchange point, a reader and writer need to meet
Thanks, ya I must be misunderstanding the async/go docs, this works fine:
(let [c (chan)] (go (>! c (+ 41 1))) (<!! c))
But I thought go returns its own channel you could wait on? Per the https://clojure.github.io/core.async/#clojure.core.async/go
Returns a channel which will receive the result of the body when
completed
I thought this meant I could feed that channel directly to <!!. But I'm clearly misunderstanding that line or how it fits into async.you can, I am not referring to the channel returned by the go block
you are are writing to a channel in the go block, but nothing is reading from the channel, so the go block waits for a reader, so the go block doesn't complete
so the channel returned from the go block never has anything written to it and is never closed
Ahhhh thanks, it's a whole other channel, so this works:
(<!! (go (+ 41 1)))
so taking from the channel returned from the go block also never completes
right ok. thanks!! sorry am fairly dense on this.
yeah, core.async is often counterintuitive, it's just more intuitive than the alternatives in its best use cases
it's a specialized tool
Hi team, in an if statement if condition is true I must return "return true" value if false I need to call a function (in an example (+ 4 5)) before returning "return false" string (value of "x" does not related to the value that must be returned when the statement is false) I found a way of implementing this but do not think this is a good approach because I do not use "x" here. What is the idiomatic way of implementing described functionality?
(if false
"return true"
(let [x (+ 4 5)]
"return false"))
(if true
"return true"
(do
(+ 4 5)
"return false"))
great thank you so much.
(or (and true "return true") (and (+ 4 5) "return false"))
(or (and true "return true") ((constantly "return false") (+ 4 5)))
this fails for examples where (+ 4 5)
is something like (println "hello")
if that is a concern then I have an expression for you