Can I use the clojure 'for' macro to modify a string?

This is a continuation of my question "Recursively change sequence in Clojure" .

Can I undo a sequence using Clojure "for a macro? I'm trying to better understand the limitations and uses of this macro.

Here is the code that I start with:

((defn reverse-with-for [s] (for [cs] c)) 

Possible?

If so, I believe that the solution may require wrapping the for macro in some expression that defines the mutable var, or that body-expr for the macro will somehow pass the sequence to the next iteration (similar to map ).

+4
source share
3 answers

Here you can change the line with:

 (defn reverse-with-for [s] (apply str (for [i (range (dec (count s)) -1 -1)] (get si)))) 

Please note that this code does not contain a mutation. This is the same as:

 (defn reverse-with-map [s] (apply str (map (partial get s) (range (dec (count s)) -1 -1)))) 

A simpler solution would be the following:

 (apply str (reverse s)) 
+5
source

Clojure for uses a macro with arbitrary Clojure sequences.

These sequences may or may not be subject to random access, like vectors. Thus, in the general case, you do not have access to the last element of the Clojure sequence without going all the way to it, which would make it impossible to pass through it in the reverse order.

I assume you had something like this (Java-like pseudocode):

 for(int i = n-1; i--; i<=0){ doSomething(array[i]); } 

In this example, we know the size of the array n in advance, and we can access the elements by its index. With Clojure sequences, we do not know this. In Java, it makes sense to do this with arrays and ArrayLists. Clojure sequences, however, are much more like linked lists - you have an element and a link to the following.

Btw, even if there is a way (possibly non-idiomatic) *, its temporal complexity will be something like O (n ^ 2), which is simply not worth the effort for a much simpler solution in a related record, which is O (n ^ 2) for lists and much better O (n) for vectors (and it is pretty elegant and idiomatic. In fact, the official reverse has this implementation).

EDIT:

General advice: do not try to do imperative programming in Clojure, it was not intended for this. Although many things may seem strange or contradictory to intuition (in contrast to the well-known idioms of imperative programming), once you get used to the functional way of doing a lot of things, I mean a lot easier.

In particular, for this question, despite the same name of Java (and other C-like) for and Clojure for , it is not the same! The first is the actual loop - it defines flow control. The second is understanding - look at it conceptually as a higher sequence function and a function f that will be executed for each of its elements, which returns another sequence f (element) s. Java for is an operator, it evaluates nothing, Clojure for (like everything else in Clojure) is an expression - it evaluates the sequence f (element) s.

Probably the easiest way to get an idea is to play with the sequence function library: http://clojure.org/sequences . In addition, you can solve some problems at http://www.4clojure.com/ . The first problems are very easy, but they gradually become more complicated as you move along them.

* As shown in Alexandre, the solution to the problem is actually idiomatic and pretty smart. Kudos for that! :)

+7
source

First of all, as Goran said, for not a statement - it is an expression, namely an understanding of consistency. It constructs sequences by iterating through other sequences. Thus, in the form it is intended for use in its pure form (without side effects). for can be thought of as an extended map introduced with filter . Because of this, it cannot be used to store the iteration state, for example. reduce do.

Secondly, you can express a sequence reversal using for and a mutable state, for example. using an atom, which is the gross equivalent (not taking into account its concurrency properties) of the java variable. But you are faced with several problems:

  • You violate the paradigm of the main language, so you will definitely look worse and behave.
  • Since all clojure mutable states of cells are designed for thread safety, they all use some kind of illegal protection against simultaneous modification, and there is no way to remove it. Consequently, you will get lower performance.
  • In this particular case, as Goran said, sequences are one of the widely used clojure abstractions. For example, there are lazy sequences that can be potentially endless, so you just can't bring them to the end. You will probably have difficulty trying to work with such sequences with imperative methods.

So do not do this, at least in clojure :)

EDIT: I forgot to mention this. for returns a lazy sequence, so you must somehow evaluate it in order to apply all the state mutations that you make in it. Another reason not to do this :)

+3
source

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


All Articles