I need help with writing a 'sequence-maybe-m' (a monad that combines the behaviour of a sequence monad with a maybe monad).
The rule should be:
If any of the inputs are nil, then the whole expression fails.
Otherwise, evaluate the body like a sequence monad would do.
(domonad sequence-maybe-m [a [1 2 3] b [1 2 3]] (+ a b))
;; => (2 3 4 3 4 5 4 5 6)
(domonad sequence-maybe-m [a [1 2 3] b nil] (+ a b))
;; => nil
(domonad sequence-maybe-m [a [1 2 3] b (range a)] (+ a b))
;; => (1 2 3 3 4 5) same as 'for'
(domonad sequence-maybe-m [a [1 2 3] b [1 nil 3]] (+ a b))
;; => nil
It'll be a bonus if it is compatible with the clojure.algo.monads library:
(defmonad sequence-maybe-m
[m-result <...>
m-bind <...>
m-zero <...>
m-plus <...>
])
where <...> are functions.
; helper function for nil-ness
(defn nil-or-has-nil? [xs] (or (nil? xs) (some nil? xs)))
; the actual monad
(defmonad sequence-maybe-m
[m-result (fn [v] [v]) ; lift any value into a sequence
m-bind (fn [mv f] ; given a monadic value and a function
(if (nil-or-has-nil? mv) ; if any nil,
nil ; result in nil
(let [result (map f mv)] ; map over valid input seq
(if (some nil? result) ; if any nils result
nil ; return nil
(apply concat result))))) ; else flatten resulting seq
m-plus (fn [& mvs] ; given a sequence of mvs
(if (some nil-or-has-nil? mvs) ; if any nil,
nil ; result in nil
(apply concat mvs))) ; otherwise, join seqs
m-zero []]) ; empty seq is identity for concatenation
The only point really worth watching out for here is the second nil-or-has-nil? in the m-bind. The first is expected - passed a monadic value, m-bind has to determine whether it's nil-ish and should immediately result in nil. The second checks the results of the computation - if it failed (producing any nil), then the overall result must be nil (as opposed to, say, the empty list resulting from (apply concat [nil nil ...])).
The output of domonad must be a monadic value, in the case of sequence-m that means it must be a sequence. Asking for an output of nil breaks that and you do not have a monad.
What you are probably looking for is adding "maybe" to the sequence-monad directly using monadic transformers, quite easy to do and described here: http://clojuredocs.org/clojure_contrib/1.2.0/clojure.contrib.monads/maybe-t.
You will want to write
(def sequence-maybe-m (maybe-t sequence-m))
where maybe-t adds the "maybe" to the sequence monad. Using this will make
(domonad sequence-maybe-m [a [1 2 3] b [1 nil 3]] (+ a b))
yield
(2 nil 4 3 nil 5 4 nil 6)
which is valid output for a monad of this type. If you need to cancel out results that have nil in them, just use some nil? on the output of the monad to check them.
Binding nil to b as you ask for in your example
(domonad sequence-maybe-m [a [1 2 3] b nil] (+ a b))
does not make sense either, since nil is not a sequence. In the transformed monad, the return value would be the empty list (). It would be more appropriate to bind [nil] to b, then you would get (nil nil nil).
It helps to remember that monads are used to compose functions of the same signature and can themselves be part of such a composition, so they must yield monadic values (in this case, sequences) themselves and in their body any binding must also be with a monadic value.
Related
I have two sequences, which can be vector or list. Now I want to return a sequence whose elements are not in common to the two sequences.
Here is an example:
(removedupl [1 2 3 4] [2 4 5 6]) = [1 3 5 6]
(removeddpl [] [1 2 3 4]) = [1 2 3 4]
I am pretty puzzled now. This is my code:
(defn remove-dupl [seq1 seq2]
(loop [a seq1 b seq2]
(if (not= (first a) (first b))
(recur a (rest b)))))
But I don't know what to do next.
I encourage you to think about this problem in terms of set operations
(defn extrasection [& ss]
(clojure.set/difference
(apply clojure.set/union ss)
(apply clojure.set/intersection ss)))
Such a formulation assumes that the inputs are sets.
(extrasection #{1 2 3 4} #{2 4 5 6})
=> #{1 6 3 5}
Which is easily achieved by calling the (set ...) function on lists, sequences, or vectors.
Even if you prefer to stick with a sequence oriented solution, keep in mind that searching both sequences is an O(n*n) task if you scan both sequences [unless they are sorted]. Sets can be constructed in one pass, and lookup is very fast. Checking for duplicates is an O(nlogn) task using a set.
I'm still new to Clojure but I think the functional mindset is more into composing functions than actually doing it "by hand", so I propose the following solution:
(defn remove-dupl [seq1 seq2]
(concat
(remove #(some #{%} seq1) seq2)
(remove #(some #{%} seq2) seq1)))
EDIT: I think it is better if we define that remove part as a local function and reuse it:
(defn remove-dupl [seq1 seq2]
(let [removing (fn [x y] (remove #(some #{%} x) y))]
(concat (removing seq1 seq2) (removing seq2 seq1))))
EDIT2: As commented by TimothyPratley
(defn remove-dupl [seq1 seq2]
(let [removing (fn [x y] (remove (set x) y))]
(concat (removing seq1 seq2) (removing seq2 seq1))))
There are several problems with your code.
It doesn't test for the end of either sequence argument.
It steps through b but not a.
It implicitly returns nil when any two sequences have the same
first element.
You want to remove the common elements from the concatenated sequences. You have to work out the common elements first, otherwise you don't know what to remove. So ...
We use
clojure.set/intersection to find the common elements,
concat to stitch the collections together.
remove to remove (1) from (2).
vec to convert to a vector.
Thus
(defn removedupl [coll1 coll2]
(let [common (clojure.set/intersection (set coll1) (set coll2))]
(vec (remove common (concat coll1 coll2)))))
... which gives
(removedupl [1 2 3 4] [2 4 5 6]) ; [1 3 5 6]
(removedupl [] [1 2 3 4]) ; [1 2 3 4]
... as required.
I test some list operation, find this difference with two syntax。
(conj (cons 321321 [1]) 123123123)
=> (123123123 321321 1)
and
(cons 321321 [1])
=> (321321 1)
(conj [321312 1] 123123123)
=> [321312 1 123123123]
why these result isn't equal?
Because you are doing different things.
cons http://clojuredocs.org/clojure.core/cons
Returns a new seq where x is the first element and seq is
the rest.
conj http://clojuredocs.org/clojure.core/conj
Returns a new collection with the xs
'added'. (conj nil item) returns (item). The 'addition' may
happen at different 'places' depending on the concrete type.
in your first example you are "prepending" a new entry (easiest way for conj to add to a sequence) and in your second example you are "appending" to a vector (again easiest way for conj to add).
user=> (.getClass (cons 321321 [1]))
clojure.lang.Cons
user=> (.getClass (conj (cons 321321 [1]) 123123123))
clojure.lang.Cons
Note you are using [...] next!
user=> (.getClass [321312 1])
clojure.lang.PersistentVector
user=> (.getClass (conj [321312 1] 123123123))
clojure.lang.PersistentVector
To pad out a sequence with some value, this is what I've come up with:
(defn pad [n coll val]
(take n (concat coll (repeat val))))
(pad 10 [1 2 3] nil) ; (1 2 3 nil nil nil nil nil nil nil)
I'm curious if there's a shorter idiom that does this already and perhaps more efficiently.
Yes this is an idiomatic way of going about padding partitions of a sequence. In fact that code is very similar to part of the partition function in clojure.core the difference being that partition does not assume a single padding value and instead asks for a sequence:
core.clj:
([n step pad coll]
(lazy-seq
...
(list (take n (concat p pad))))))))
You can get the same results by passing a padding collection to partition:
user> (defn pad [n coll val]
(take n (concat coll (repeat val))))
#'user/pad
user> (pad 10 [1 2 3] nil)
(1 2 3 nil nil nil nil nil nil nil)
user> (first (partition 10 10 (repeat nil) [1 2 3]))
(1 2 3 nil nil nil nil nil nil nil)
Here's a lazy version of the padding function:
(defn lazy-pad
"Returns a lazy sequence which pads sequence with pad-value."
[sequence pad-value]
(if (empty? sequence)
(repeat pad-value)
(lazy-seq (cons (first sequence) (lazy-pad (rest sequence) pad-value)))))
You can use it like a regular infinite lazy collection:
(take 5 (lazy-pad [1 2 3] :pad))
=> (1 2 3 :pad :pad)
IMO it's more elegant this way. You can also use it with other functions which expect a lazy sequence, which doesn't work if you have to specify the length upfront:
(partition 2 (interleave [1 2 3 4] (lazy-pad [:a] :pad)))
=> ((1 :a) (2 :pad) (3 :pad) (4 :pad))
In this discussion Brian Marick makes the point that let and for are monads in Clojure:
That said, the really general-purpose monads tend to get written into the language as special forms. Clojure's let and for are both monads, but you don't need to know that to use them.
This is let
user=> (let [c (+ 1 2)
[d e] [5 6]]
(-> (+ d e) (- c)))
8
This is for
user=> (for [x [0 1 2 3 4 5]
:let [y (* x 3)]
:when (even? y)]
y)
(0 6 12)
My question is: Why are Clojure's let and for both monads?
Why are Clojure's let and for both monads?
They aren't.
Clojure's let and for are not monads because they do not fully expose their Monadic common structure. They are more like monads in a sugary prison.
What are monads?
In Clojure parlance, a monad could be described as reification of a Monad protocol whose functions are expected to behave with each other and on the reified type in certain well defined ways. This is not to say that monads have to be implemented with defprotocol, reify, and friends, but this gives the idea without having to talk about typeclasses or categories.
(defprotocol Monad
(bind [_ mv f])
(unit [_ v]))
(def id-monad
(reify Monad
(bind [_ mv f] (f mv))
(unit [_ v] v)))
(def seq-monad
(reify Monad
(bind [_ mv f] (mapcat f mv))
(unit [_ v] [v])))
Sugar
Monads can be messy to use
(bind seq-monad (range 6) (fn [a]
(bind seq-monad (range a) (fn [b]
(unit seq-monad (* a b))))))
;=> (0 0 2 0 3 6 0 4 8 12 0 5 10 15 20)
Without some sugar
(defn do-monad-comp
[monad body return]
(reduce
(fn [a [exp sym]] (list 'bind monad exp (list 'fn [sym] a)))
(list 'unit monad return)
(partition 2 (rseq body))))
(defmacro do-monad [monad body return]
(do-monad-comp monad body return))
This is easier to write
(do-monad seq-monad
[a (range 6)
b (range a)]
(* a b))
;=> (0 0 2 0 3 6 0 4 8 12 0 5 10 15 20)
But isn't that just...?
This looks a lot like
(for
[a (range 6)
b (range a)]
(* a b))
;=> (0 0 2 0 3 6 0 4 8 12 0 5 10 15 20)
And
(do-monad id-monad
[a 6
b (inc a)]
(* a b))
;=> 42
Looks a lot like
(let
[a 6
b (inc a)]
(* a b))
;=> 42
So, yes, for is like the sequence monad and let is like the identity monad, but in the confines of a sugared expression.
But that's not all monads are.
Monads' structure/contract can be exploited in other ways. Many useful monadic functions can be defined in terms of only bind and unit, for example
(defn fmap
[monad f mv]
(bind monad mv (fn [v] (unit monad (f v)))))
So that they can be used with any monad
(fmap id-monad inc 1)
;=> 2
(fmap seq-monad inc [1 2 3 4])
;=> (2 3 4 5)
This might be a rather trivial example, but more generally/powerfully monads can be composed, transformed, etc. in a uniform way due to their common structure. Clojure's let and for don't fully expose this common structure, and so cannot fully participate (in a generic fashion).
I would say it's more correct to call let and for do-notation for the identity monad and the list monad, respectively - they're not themselves monads, since they're bits of syntax rather than data structures with associated functions.
The reason monads come up at all is that for is a nice notation that exploits the monadic behavior of lists (or in clojure, sequences) to easily write code that does some useful things.
let is the identity monad. There is no special machinery, each binding is just made available to the subsequent stages of the computation.
for is the list / sequence monad with guards, plus a little something extra. Here each step in the computation is expected to produce a sequence; the machinery of the monad takes care of concatenating sequences. Guards can be introduced with :when, while :let introduces intermediate helper bindings (as let does in Haskell). The "something extra" comes in the form of :while.
Look at the function below. I want to pass a vector of factors and test if any of the elements in the vector is a factor of x. How do I do that?
(defn multiple?
"Takes a seq of factors, and returns true if x is multiple of any factor."
([x & factors] (for [e m] ))
([x factor] (= 0 (rem x factor))))
You could try using some and map:
(defn multiple? [x & factors]
(some zero? (map #(rem x %) factors)))
Also some returns nil if all tests fail, if you need it to actually return false, you could put a true? in there:
(defn multiple? [x & factors]
(true? (some zero? (map #(rem x %) factors))))
Note that some short-circuits and map is lazy, so multiple? stops as soon as a match is found. e.g. the following code tests against the sequence 1,2,3,4,....
=> (apply multiple? 10 (map inc (range)))
true
Obviously this computation can only terminate if multiple? doesn't test against every number in the sequence.
You can solve it only using some.
=> (defn multiple? [x factors]
(some #(zero? (rem x %)) factors))
#'user/multiple?
=> (= true (multiple? 10 [3 4]))
false
=> (= true (multiple? 10 [3 4 5 6]))
true
some will stop at the first factor.
Try this, using explicit tail recursion:
(defn multiple? [x factors]
"if any of the elements in the vector is a factor of x"
(loop [factors factors]
(cond (empty? factors) false
(zero? (rem x (first factors))) true
:else (recur (rest factors)))))
The advantages of the above solution include: it will stop as soon as it finds if any of the elements in the vector is a factor of x, without iterating over the whole vector; it's efficient and runs in constant space thanks to the use of tail recursion; and it returns directly a boolean result, no need to consider the case of returning nil. Use it like this:
(multiple? 10 [3 4])
=> false
(multiple? 10 [3 4 5 6])
=> true
If you want to obviate the need to explicitly pass a vector (for calling the procedure like this: (multiple? 10 3 4 5 6))) then simply add a & to the parameter list, just like it was in the question.
A more Clojurian way is to write a more general-purpose function: instead of answering true/false question it would return all factors of x. And because sequences are lazy it is almost as efficient if you want to find out if it's empty or not.
(defn factors [x & fs]
(for [f fs :when (zero? (rem x f))] f))
(factors 5 2 3 4)
=> ()
(factors 6 2 3 4)
=> (2 3)
then you can answer your original question by simply using empty?:
(empty? (factors 5 2 3 4))
=> true
(empty? (factors 6 2 3 4))
=> false