块封装与本地封装 - 让

Posted

技术标签:

【中文标题】块封装与本地封装 - 让【英文标题】:Block encapsulation vs. local encapsulation - let 【发布时间】:2012-10-23 11:43:13 【问题描述】:

当我拥有与独立于其参数的函数相关的数据时,我应该在什么时候支持块封装而不是本地封装?

我应该什么时候使用:

(let [hello "Hello "]
  (defn do-greet
    "Print a greeting."
    [name]
    (println (str hello name))))

对比:

(defn do-greet
  "Print a greeting."
  [name]
  (let [hello "Hello "]
    (println (str hello name))))

【问题讨论】:

【参考方案1】:

如果您想在词法范围的代码块中使用静态常量之类的值,则前者是一个合理的选择。通常你会这样做:

该值的计算成本很高,并且您只想在加载命名空间时执行一次 该值是真正的常量,即不会在函数调用之间改变 该值将用于多个函数定义(即您将多个定义放在 let 块中) (可能?)因为您想在宏扩展中使用值,而在宏扩展中嵌入 let 会增加不必要的复杂性。

在大多数其他情况下,可能应该首选后一个版本,这有几个原因:

比较地道 它允许函数定义在顶层,这对代码的可读性/理解性更好 它允许值随着不同的函数调用而变化 它更好地反映了在本地上下文中使用值的意图

【讨论】:

我喜欢这个,你涵盖了更多的点。我不确定您的宏扩展点是否有效,因为您可以将 let 包裹在扩展周围,但仍位于函数内部。为了可读性,我建议将风格选择与语义差异分开。 (值可能因不同的函数调用而异)。【参考方案2】:

这是一种风格选择,并且应该至少在一定程度上取决于计算价值的成本。请考虑:

(defn nth-prime [n] ...)

(defn f [x]
  (let [factor (nth-prime 10000)]
    (* x factor)))

(let [factor (nth-prime 10000)]
  (defn g [x]
    (* x factor)))

每次调用 f 时重新计算一个昂贵的常量是浪费的,g 使用一种简单的技术来避免这样做。

【讨论】:

通过说运行时行为(性能)存在差异,我认为这不是一种风格选择。只是因为在提问者的例子中,实际上没有区别,它是风格的。【参考方案3】:

如果hello 仅用于该单个函数,则将let 放在函数本身内更有意义。如果您要在多个函数中使用hello,那么将let 放在这些函数之外并包裹在这些函数之外是有意义的。

【讨论】:

如果两者在运行时不等价,我认为我们不应该仅仅根据风格来决定。想象一下,绑定是读取静态资源(let [hello (slurp (clojure.java.io/resource "hello"))] ...)。我的问题更笼统,因为我不知道这些表格实际上是如何评估的以及相应的权衡是什么。 在某些情况下,它与风格没有任何关系。如果您需要该值在两个不同的函数中可用,则必须使该值在这些函数的范围之外可用。另外,请参阅@amalloy 的回答。【参考方案4】:

肯定是这样的:

(defn do-greet
  "Print a greeting."
  [name]
  (let [hello "Hello "]
    (println (str hello name))))

【讨论】:

以上是关于块封装与本地封装 - 让的主要内容,如果未能解决你的问题,请参考以下文章

数据封装与解封装

Vue的Vue-ls 封装的本地存储的方法

JS代码的位置与事件响应代码块的封装问题

Android通用搜索页的分析与封装

Android通用搜索页的分析与封装

20170817 - 今日技能封装 - Q