How to rewrite Ruby `Array (x)` in Clojure?

In Ruby, there is the Kernel#Array method, which acts as follows:

 Array([1, 2, 3]) #=> [1, 2, 3] Array(1..5) #=> [1, 2, 3, 4, 5] Array(9000) #=> [9000] Array(nil) #=> [] 

In other words, it converts nil to empty arrays, non-collections to singleton arrays, and object types similar to collections (i.e. objects that respond to #to_ary or #to_a ) to arrays.

I want to have something like this in Clojure:

 (to-seq [1 2 3]) ;=> (1 2 3) (to-seq (range 1 5)) ;=> (1 2 3 4) (to-seq 9000) ;=> (9000) (to-seq nil) ;=> nil; () is ok too 

What I have so far:

 (defn to-seq [x] (if (seqable? x) (seq x) '(x))) 

But I do not like it because:

  • seqable? function seqable? evaporated during the explosion of a monolithic clojure -contrib. I do not want to include a huge ancient library that does not support more time in my project for only one function.
  • I have a feeling that there should be a built-in function that is either in clojure.core or in clojure.contrib.whatever. Or that there is a more idiomatic approach.

The exact type of to-seq output is not important. The main thing is that I would like to use its output, for example, in lists:

 (for [person (to-seq person-or-people)] ...) 

So, if there is a vector - this is normal. If it is a vector, a Java array, a list, or zero depending on the input, this is also good.

==== UPD ====

Almost final version:

 (defn to-seq [x] (if (or (nil? x) (coll? x)) x [x])) 

Final version:

(defn to-seq ..blabla..) Strike>

I don’t need to-seq, it does not look attractive.

+1
source share
2 answers

If you only need to iterate over the input data, and this is actually some kind of container, you usually do not need to perform any special conversions. Two special cases that come to mind are handled by enumeration-seq and iterator-seq .

I believe that there is really no ready-made function in the kernel to determine if an object is a valid input for seq . The corresponding code is in the clojure.lang.RT seq and seqFrom and basically tries to use several features in turn; I find it seqable? tested the same possibilities - you could do the same. Existing functions like coll? , seq? sequential? , cannot catch at least some of them (especially arrays and strings); coll? probably closest sequential? flatten is especially used (therefore, it is limited in its ability to actually smooth things).

Of course, there is always the opportunity to try applying seq and catch the exception (not what I would recommend):

 (defn seq-at-all-costs [x] (try (seq x) (catch IllegalArgumentException e (list x)))) ; NB. '(x) wouldn't work, since it would ; cause a literal symbol x to be returned, ; as noted in my comment on the question 

You can also do something similar to speed things up (I hope I would definitely set myself the task):

 (defprotocol IKnowHowToSeqYou (seq-knowledgeably [this])) (defn seq! [x] (try (seq-knowledgeably x) (catch IllegalArgumentException e (try (seq x) (extend-type (class x) IKnowHowToSeqYou (seq-knowledgeably [this] (seq this))) (catch IllegalArgumentException e (extend-type (class x) IKnowHowToSeqYou (seq-knowledgeably [this] (list this))))) (seq-knowledgeably x)))) 

I have to say, however, that I almost never - perhaps never - feel the need for seqable? . The reason is probably because it is relatively unusual that I do not know at all what types of objects I will deal with at any time, and in those rare cases, as a rule, I look through cases / apply protocols / use ellipses, etc. d.


Here is the checklist used by RT.seq and RT.seqFrom at the moment:

  • if the input is already seq (which for RT.seq means " clojure.lang.ASeq instance", not ISeq ), it is returned unchanged;

  • if he is a lazy seq, he is forced and the result is returned;

  • otherwise, if it implements Seqable , then he asked to create seq of its contents;

  • if it is nil ( null ), nil returned;

  • if Iterable , the iterator seq is returned;

  • if it is an array, an array of seq is returned;

  • if it is CharSequence , the string seq is returned;

  • if it is java.util.Map , seq is returned over its recordset;

  • otherwise an exception is thrown.

+3
source
 (map coll? [() [] #{} {} 1 "sandwich" :a nil])) ;=> (true true true true false false false false) (defn to-seq [x] (if (coll? x) (seq x) (list x))) 

coll? use?

See also: seq? or sequential?

+2
source

Source: https://habr.com/ru/post/952898/


All Articles