需要强制实现懒惰seqs之前/之后元素方式的命令式操作? [英] Need to force realization of lazy seqs before/after element-wise imperative operations?

查看:137
本文介绍了需要强制实现懒惰seqs之前/之后元素方式的命令式操作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我使用 map 对延迟序列的每个成员特定的个别数据结构执行副作用/变异操作,我需要(a)调用 doall 首先,在执行命令式操作之前强制实现原始序列,或者(b)调用 doall - 在对所得序列映射功能操作之前会发生什么?

If I perform a side-effecting/mutating operation on individual data structures specific to each member of lazy sequence using map, do I need to (a) call doall first, to force realization of the original sequence before performing the imperative operations, or (b) call doall to force the side-effects to occur before I map a functional operation over the resulting sequence?

我相信没有 doall 当任何序列的元素之间没有依赖性时是必需的,因为 map 不能将函数应用于序列的成员,直到函数产生该序列的 map s已应用于较早序列的相应元素。因此,对于每个元素,函数将以适当的顺序应用,即使其中一个函数产生稍后函数依赖的副作用。 (我知道我不能假设任何元素 a 在元素 b 之前已经被修改,但这并不重要。)

I believe that no doalls are necessary when there are no dependencies between elements of any sequence, since map can't apply a function to a member of a sequence until the functions from maps that produced that sequence have been applied to the corresponding element of the earlier sequence. Thus, for each element, the functions will be applied in the proper sequence, even though one of the functions produces side effects that a later function depends on. (I know that I can't assume that any element a will have been modified before element b is, but that doesn't matter.)

这是否正确?

这是问题,如果它足够清楚,那么就不需要进一步阅读了。

That's the question, and if it's sufficiently clear, then there's no need to read further. The rest describes what I'm trying to do in more detail.

我的应用程序有一系列defrecord结构(代理),每个包含一些核心向量( vec1 vec2 )和一个core.matrix矩阵 mat )。假设为了速度的缘故,我决定(破坏性地,非功能性地)修改矩阵。

My application has a sequence of defrecord structures ("agents") each of which contains some core.matrix vectors (vec1, vec2) and a core.matrix matrix (mat). Suppose that for the sake of speed, I decide to (destructively, not functionally) modify the matrix.

程序通过调用每个代理执行以下三个步骤 map 三次,将每个步骤应用到每个代理。

The program performs the following three steps to each of the agents by calling map, three times, to apply each step to each agent.


  1. 在每个代理中使用 assoc

  2. 在每个代理中使用 基于前面的向量,每个代理中的矩阵 mat (即矩阵将保持不同的状态)。

  3. 更新向量<$基于第2步生成的矩阵的状态,使用 assoc 在每个代理中创建一个c $ c> vec2

  1. Update a vector vec1 in each agent, functionally, using assoc.
  2. Modify a matrix mat in each agent based on the preceding vector (i.e. the matrix will retain a different state).
  3. Update a vector vec2 in each agent using assoc based on the state of the matrix produced by step 2.

例如, persons 是一个序列,可能是lazy(EDIT:Added outer doall s):

For example, where persons is a sequence, possibly lazy ( Added outer doalls):

(doall
  (->> persons
    (map #(assoc % :vec1 (calc-vec1 %)))            ; update vec1 from person
    (map update-mat-from-vec1!)                     ; modify mat based on state of vec1
    (map #(assoc % :vec2 (calc-vec2-from-mat %))))) ; update vec2 based on state of mat

或者:

(doall
  (map #(assoc % :vec2 (calc-vec2-from-mat %))     ; update vec2 based on state of mat
       (map update-mat-from-vec1!                  ; modify mat based on state of vec1
            (map #(assoc % :vec1 (calc-vec1 %)) persons)))) ; update vec1 from person

请注意,代理的状态取决于任何 em>任何时候。我需要添加 doall 吗?

Note that no agent's state depends on the state of any other agent at any point. Do I need to add doalls?

strong>截至2014年4月16日的回答概述:

Overview of answers as of 4/16/2014:

我建议阅读所有给出的答案,但看起来好像它们有冲突。他们不,我认为如果我总结主要想法可能是有用的:

I recommend reading all of the answers given, but it may seem as if they conflict. They don't, and I thought it might be useful if I summarized the main ideas:

(1)我的问题的答案是是:如果,我所描述的过程的结束,一个导致整个延迟序列被实现,然后根据正确的步骤序列(1,2,3)对每个元素进行什么操作。没有必要在步骤2之前或之后应用 doall ,其中每个元素的数据结构都是可变的。

(1) The answer to my question is "Yes": If, at the end of the process I described, one causes the entire lazy sequence to be realized, then what is done to each element will occur according to the correct sequence of steps (1, 2, 3). There is no need to apply doall before or after step 2, in which each element's data structure is mutated.

(2)但是:这是一个很糟糕的主意;你在未来要求麻烦。如果在某些时候,你不经意地结束了在除了你原来想要的时间之外的时间实现全部或部分序列,那么后面的步骤可能会从错误的时间处在那里的数据结构中得到值 - 在不是你期望的时间。变换每个元素的数据结构的步骤不会发生,直到实现了延迟seq的给定元素,所以如果你在错误的时间实现它,你可能在后面的步骤中得到错误的数据。这可能是那种很难跟踪的错误。 (感谢@ A.Webb让这个问题非常清楚。)

(2) But: This is a very bad idea; you are asking for trouble in the future. If at some point you inadvertently end up realizing all or part of the sequence at a time other than what you originally intended, it could turn out that the later steps get values from the data structure that were put there at at the wrong time--at a time other than what you expect. The step that mutates a per-element data structure won't happen until a given element of the lazy seq is realized, so if you realize it at the wrong time, you could get the wrong data in later steps. This could be the kind of bug that is very difficult to track down. (Thanks to @A.Webb for making this problem very clear.)

推荐答案

混合懒惰和副作用

(defrecord Foo [fizz bang])

(def foos (map ->Foo (repeat 5 0) (map atom (repeat 5 1))))

(def foobars (map #(assoc % :fizz @(:bang %)) foos))

现在foobars的fizz现在是1吗?

So will my fizz of foobars now be 1?

(:fizz (first foobars)) ;=> 1

酷,现在我会离开foobars单独工作与我的原始foos ...



Cool, now I'll leave foobars alone and work with my original foos...

(doseq [foo foos] (swap! (:bang foo) (constantly 42)))

让我们检查foobars

Let's check on foobars

(:fizz (first foobars)) ;=> 1
(:fizz (second foobars)) ;=> 42

很糟糕...

,为您的副作用使用 doseq 而不是 map 或意识到延迟您的副作用直到实现的后果。

Generally, use doseq instead of map for your side effects or be aware of the consequences of delaying your side effects until realization.

这篇关于需要强制实现懒惰seqs之前/之后元素方式的命令式操作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆