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

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

问题描述

如果我使用 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 不能应用函数到序列的成员,直到产生该序列的 maps 中的函数已应用于较早序列的相应元素.因此,对于每个元素,函数将以正确的顺序应用,即使其中一个函数会产生后续函数所依赖的副作用.(我知道我不能假设任何元素 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 结构(代理"),每个结构都包含一些 core.matrix 向量(vec1vec2)和一个 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更新每个代理中的向量vec1.
  2. 根据前面的向量修改每个代理中的矩阵 mat(即矩阵将保持不同的状态).
  3. 根据步骤 2 生成的矩阵的状态,使用 assoc 更新每个代理中的向量 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 是一个序列,可能是懒惰的(添加了外部 doalls):

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

请注意,任何时候都没有代理的状态依赖于任何其他代理的状态.我需要添加 doalls 吗?

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

截至 2014 年 4 月 16 日的答案概述:

我建议阅读所有给出的答案,但它们似乎相互冲突.他们没有,我认为如果我总结一下主要想法可能会有用:

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) 但是:这是一个非常糟糕的主意;你是在自找麻烦.如果在某些时候您无意中最终在与最初预期不同的时间实现了全部或部分序列,结果可能是后面的步骤从数据结构中获取了在错误时间放置在那里的值——在与您预期不同的时间.在实现惰性序列的给定元素之前,不会发生改变每个元素数据结构的步骤,因此如果您在错误的时间意识到它,您可能会在后面的步骤中获得错误的数据.这可能是一种很难追踪的错误.(感谢@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.)

推荐答案

使用极度谨慎将懒惰与副作用混为一谈

Use extreme caution mixing laziness with side effects

(defrecord Foo [fizz bang])

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

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

那么我的 foobars 现在会变成 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.

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

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