请分享您的想法在 2024 年 Clojure 调查问卷!

欢迎!请参阅 关于 页面以获取更多有关此知识库的信息。

+8
协议
重新标记了

clojure.protocols/nav 函数的签名是

[coll k v]

我不懂 collkv 该是什么。能解释一下吗?也许可以给出一个带有实际参数调用 nav 的示例?

例如,coll 是什么。它是前期调用 datafy 的结果吗?因此,coll 就是我们要导航进入的对象,还是说那是 v

我认为 k 是用于深入挖掘所需的信息。

现在有 v,所以,我又不确定这是否表示我们要导航进去的值。如果是这样,那么 coll 又是什么?如果 v 不是这样,那么我不明白它应该是什么。难道不是值是我们希望 nav 返回的,为什么要同时提供它?

谢谢

2 个答案

+6

让我们先假设我们有一个类型为 Thing 的对象,并且它有一个实现 datafy 的实例,该实例生成一个描述该对象的 Clojure 哈希表。

myThing 映射到 datafy,得到 description-hash-map

现在我们可以用 get 来导航这个哈希表:(get description-hash-map :foo),这将产生一些值,我们将称之为 foo-val

通过使用datafyThing世界转换到Clojure数据世界,我们可以使用所有常规的Clojure函数在数据世界中导航,但在数据世界元素所表示的点,通常需要一种方法返回到Thing世界。

这就是nav的作用。对于任何产生一些foo-val值的(get description-hash-map :foo),我们可以通过调用(nav description-hash-map :foo foo-val)来返回到Thing世界中的等效点,这将产生Thing世界中的新对象。

然后我们可以通过在新的对象上调用datafy来返回到Clojure数据世界,使用get在数据世界中导航,使用nav返回到Thing世界中的等效点。

myThing 映射到 datafy,得到 description-hash-map

description-hash-map -> get -> more-data

description-hash-map -> nav (带有more-data) -> someNewThing

如果datafy产生一个向量,则可以使用向量、索引(向量可以根据它们的索引关联)和该索引处的元素来调用nav

在任何情况下,nav可能关心或不关心值参数(实际上,它可能也关心或不关心键参数...但这不太可能)。请注意,当datafy产生一些对象的表示形式时,它将原始对象作为元数据添加到该表示形式上——并且这组元数据可通过coll参数提供给nav以便它可以在需要时访问原始对象。

如果您想在现实世界中查看具体的示例,请参阅next.jdbc的datafynav:schema机制

by
我研究了next.jdbc的实现,并且发现在`nav`的实现中从未使用过`coll`: https://github.com/seancorfield/next-jdbc/blob/1b93d3a04ba033bb627e493df28f32330dfeb2e5/src/next/jdbc/result_set.clj#L643-L664

这让我更迷惑了 :(
by
是的,在这种情况下,只使用键和值就能找到“新的Thing”——因为外键关系是基于键和值的(以及可能还有模式选项),而不是找到它的数据行。

因此,nav可能或可能不关心值、键,甚至是集合 :)
 by
在导航过程中,"coll"通常很方便用来存储元信息。
 by
那么,我总结一下:

- “v”是我们导航到的目标。
- "coll"应该是调用“datafy”后的结果。
- "v"应该在"coll"中,可以被“k”检索到。

因此,一个使用场景可能是:

(let [coll (datafy some-obj)
      k :next-thing
      v (get coll k)]
  (nav coll k v))

这将返回另一个东西(也就是任意可数据化的对象),这是从导航进入“v”中获得的。

在这种情况下,"v"应该是一个超链接,它实际上不是集合,这就是我们为什么导航到超链接指向的地方,返回另一个东西我们可以稍后再次数据化,也许还可以进一步导航。

所以一个完整的例子可以是:

(let [url (as-url "https://clojure.org")
      clojure-page-as-coll (datafy url) ...

此时,根据URL的数据化实现,我们可以假设我们得到了以下内容

{:content
 :links {:get-started (as-url "https://clojure.org/guides/getting_started")
         :overview (as-url "https://clojure.org/about/rationale")
         ...}
...}

如果我们想导航到[:links :overview],我们会这样做

(let [url (as-url "https://clojure.org")
      clojure-page-as-coll (datafy url)
      overview-url (get-in coll [:links :overview])
      overview-page (nav clojure-page-as-coll [:links :overview] overview-url) ...

所以到了这一步,我们已经将URL数据化为Clojure集合。并且我们使用正常的Clojure集合函数对其进行了深入挖掘。当我们达到另一个可导航值时,我们调用nav来获取下一页。

我只有一点不太明白。如果你看到我的例子,我假设nav返回一个数据化的结果。但Sean说它不应该这样。但这在我的例子中甚至都不合理。我从URL开始。我可以导航到那个URL以获取页面,但没有“coll”上下文。并且我可以让nav返回非数据化的东西,但我没有这样的东西,仅因其奇 modulus而编造一个似乎很奇怪。

所以我这里假设nav可以返回数据或可数据化的东西,这并不重要。并且假设无论如何都会调用datafy,如果它已经是数据,datafy将仅返回数据,因为这是Map的默认实现。

所以你会继续这样做

(let [url (as-url "https://clojure.org")
      clojure-page-as-coll (datafy url)
      overview-url (get-in coll [:links :overview])
      概览页面(nav clojure-page-as-coll [:links :概览] overview-url)
      概览页面-as-coll(datafy 概览页面)
  概览页面)

这是正确的吗?还是你应该实际从 `nav` 开始?或者可能我的例子不好,因为在这种情况下,

(let [clojure-page (nav nil nil (as-url "https://clojure.org")))
      overview-url (get-in clojure-page [:links :概览])
      概览页面 (nav clojure-page [:links :概览] overview-url)]
  概览页面)

感谢!
我预期 nav 应该匹配 get,而不是 get-in,并且我预期它应以数据类型为基础工作(因为它是基于协议的)。

(注释不适合分享代码,所以我不会尝试,但我将基于你的 URI 导航构建一个示例,稍后回来)
"我假设 `nav` 返回一个 datafied 结果。"

不,应该返回一个对象。

"我可以 nav 到那个 URL 以获取页面回退"

不,你应该先 datafy URL 以获取数据,然后 nav 到这些数据中(这里有你的集合)。

"我可以将 nav 返回的东西变为非 data,但我没有这样的东西,只是随意创建一个似乎有点奇怪。"

如果你已经有数据,完全可以从 nav 中返回它。但我想你可能希望返回一个 "as-url" 对象,无论它的形式如何。
> 不,你应该先 datafy URL 以获取数据,然后 nav 到这些数据中(这里有你的集合)。

当你说到“nav”进数据时,我会感到困惑,因为集合可以被导航到,例如获取(get)、进入(get-in)、第一个(first)、第n个(nth)等都可以做到。以便导航到数据的元素或其内部的嵌套集合。但除此之外,如果元素是超链接的一种形式,则可以导航到元素,因为它需要从远程获取剩余的内容并/或进行渲染。

起初我认为“nav”做了两件事,甚至把结果数据化了。但现在看起来“nav”只应该做后者?

如果“nav”只做后者,那么将要有很多用例,它将被证明是无用的吗?就像一个恒等函数一样。例如,在没有需要获取或渲染的情况下,只需将对象转换为数据,或者对象中嵌套对象的情况。

因此,我认为“datafy”不应该过深。但这是否正确?是否应该深层且递归式地数据化整个对象及其所有包含的对象,除非需要从远程获取更多数据或进行渲染的情况?
by
事物世界 -> datafy -> Clojure数据世界

Clojure数据世界 -> get等 -> Clojure数据世界

Clojure数据世界 -> nav -> 事物世界

在这里,重要的是“世界”的改变。

数据化(datafy)或导航(nav)中的任何一个都能执行一些复杂的转换来转换世界。

例如,(datafy (java.net.URL "..."))可能将Java对象转换成该URL的内容,并以数据的形式渲染。

事物世界 -> datafy -> Clojure数据世界。

Clojure在数据中的导航可以让你获取到页面中的每个链接(作为Clojure数据结构,如{:link "..."}表示)。

Clojure数据世界 -> get等 -> Clojure数据世界

随后,可以将nav应用于这些相同的路径,将{:link "..."}转换为java.net.URL对象。

Clojure数据世界 -> nav -> 事物世界

现在,我们回到了事物世界,有一个java.net.URL对象...我们可以数据化它以获取引用内容的结构化数据,并回到Clojure数据世界。

如果您还没有尝试REBL,我强烈建议您这么做——这真的会让这一切变得更加清晰(至少,我希望会是这样的)。
+1投票
by

nav是一个通用的导航框架。给定一个“容器”(coll)和容器中的某些选择,将其导航到该选择。对于容器,k是有意义的(如字典),对于其他一些情况,则不需要选择值。作为一个通用框架,两者都提供给你,因为你可能需要两者来完成选择。

至于输入和输出的具体内容,这是一个完全依赖于上下文的答案。nav是一个适合当前目的的工具,除非知道特定的目标,否则无法给出具体答案。通常,你是nav的调用者和实现者(通过实例元数据),重要的是它们必须匹配。

在REBL中,nav用于浏览操作,当你左窗格中选择了某个项目并“浏览”它时。其他用途可能做不同的事情(序列化对象图,等等)。

by
我曾经认为,为我拥有的东西添加datafy/nav的支持将会使其在REBL内也得到支持。是这样的吗?因为我假设“钻入”应该有一个约定,比如说,当你评估某个表达式时,datafy会被调用并在内容窗格中渲染结果,如果它是REBL可以选中的已知类型,比如映射或矢量,选中集合中的idx并向前导航将以`k`为idx和`v`为val调用datafy的expr,然后这个结果将变成新的expr,然后它将重复。

我可以看到datafy/nav可以非常通用,基本上可以根据你的个人约定使用它,但是在REBL的上下文中,有没有什么约定可以遵循的?

再次感谢!
by
编辑 by
我实际上在REBL中尝试了这一点,看起来是这样的

1. 内容窗格对选定的表达式调用datafy
2. 向前导航调用nav,其中`coll`是第1步中datafy的返回值
`k`是内容窗格中选中的`idx`
`v`是内容窗格中选中的`val`

从那以后和这里所有的答案,我了解更多关于这个摘要 https://gist.github.com/didibus/2a2a62d365f93d55db4fb27f46ecef89 看起来是datafy/nav的一个合理使用案例?
by
在您的示例中不需要使用nav: https://github.com/seancorfield/datafy-nav-example/blob/master/src/datafy_test/alt.clj

这样做会产生与实现nav完全相同的结果(因为没有实现nav,因为nav对一个哈希表的默认行为只是返回值v,即(get coll k),因为这就是你在这个上下文中调用nav的方式)。
...