请在 2024 年 Clojure 状态调查! 中分享您的想法。

欢迎!请查看关于页面,了解更多关于这个站点如何工作的信息。

+8
协议
重新标记

clojure.protocols/nav 函数的签名是

[coll k v]

我不懂 collkv 应该是什么。有人能解释一下吗?也许能提供一个带有实际参数的 nav 调用示例?

例如,什么是 coll。它是否应该是之前的 datafy 调用的结果?因此,《coll》是我们将要导航的对象,还是那是 《v》?

我假设 k 是钻取所需的信息。

现在有 v,所以我仍然不确定这是否是我们的导航值。如果是这样,那么 《coll》 又是什么?如果 《v》 不是这个,那么我不明白它应该是什么。难道不是我们想要的返回值吗?为什么还要提供它?

谢谢

2 答案

+6

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

myThing -> datafy -> description-hash-map

现在我们可以使用 get 来遍历该哈希表:(get description-hash-map :foo),它会产生一个我们叫做 foo-val 的值。

通过 datafyThing 世界进入 Clojure 数据世界后,我们可以使用所有常规的 Clojure 函数在 数据世界 中导航,但我们通常需要一个方法来回导航到由数据世界中的元素表示的点。

这时就轮到 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 (with more-data) -> someNewThing

如果 datafy 产生了一个向量,你用向量、索引(向量按索引关联)和该索引处的元素来调用 nav

在所有情况下,nav 可能会也可能不会关心值参数(还有,实际上,它可能也会也可能不会关心键参数……但这不太可能)。请注意,当 datafy 产生某个对象的表示形式时,它会将原始对象作为元数据添加到表示形式上——而这个元数据可以通过 coll 参数提供给 nav,这样它就可以获取原始对象(如果需要的话)。

如果你想看看真实世界中的具体示例,请参阅 next.jdbc 的 datafynav:schema 功能

by
我查看了 next.jdbc 的实现,并且发现 collnav 的实现中从未被使用:https://github.com/seancorfield/next-jdbc/blob/1b93d3a04ba033bb627e493df28f32330dfeb2e5/src/next/jdbc/result_set.clj#L643-L664

这让我更加困惑 :(
by
确实如此。在这种情况下,可以通过只使用键和值来找到“新的 Thing”——因为外键关系是建立在键和值基础上(以及可能的模式选项),而不是它所在的行数据。

导航可能或可能不关心值、键甚至整个集合:)
在导航过程中,集合(coll)通常是一个存储所需元数据的好地方。
好的,然后我总结了以下几点

- `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 返回不是已经数据化的东西,但没有这样的东西,只是因为想它是奇怪的。

我在这里假设`nav`可以返回数据或数据化的事物,实际上这并不重要。无论怎样,都会对它调用`datafy`函数,但如果它已经是数据,`datafy`就会直接返回数据,因为这是Map的默认实现。

所以你继续这样做

(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)
      overview-page-as-coll (datafy overview-page)]
  overview-page)

这是正确的吗?或者你应该实际从`nav`开始?或者我的例子可能不太合适,因为我发现`datafy`在这种情况下似乎是多余的,因为有人可能只是这样做

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

谢谢!
by
我期望nav与get匹配,而不是get-in,以及我期望它是基于数据类型的(因为是协议驱动的)。

(评论在分享代码时非常差劲,所以我不会尝试,但我将基于你的URI导航构建一个例子,稍后再回来)
by
“我假设`nav`返回一个数据化的结果。”

不,应该返回一个对象。

“我可以通过nav进入该URL获取页面。”

不,你应该先数据化URL来获取数据,然后nav进入该数据(这就是你的coll)。

“我也可以让nav返回非数据的内容,但我没有这样的东西,只是随便编造一个听起来很奇怪。”

如果你已经有了数据,从nav中返回它是完全正确的。但这里我认为你想返回一个“as-url”对象,无论这个对象是什么形式。
by
> 不,你应该先数据化URL来获取数据,然后nav进入该数据(这就是你的coll)。

当你说“nav into”时,我感到困惑,因为集合可以导航进入,例如什么get、get-in、first、nth等常用的函数。为了导航到数据元素或其内部的嵌套集合。但它们也可以作为超链接形式进行导航,因为其它的内容需要远程获取或渲染。

起初我以为nav两者都能做,并使结果数据化。但现在看起来nav可能只做后者?

如果nav只做后者,那么会有很多用例将变得无用,正确的吗?就像恒等函数。例如,当不需要获取或渲染任何内容,只需将数据转换为数据结构时。例如,当你有嵌套在对象中的对象时?

对于这一点,我认为datafy不应该递归。但这是不是真的?datafy深度递归地数据化整个对象以及所有包含的对象,除非需要远程获取或渲染更多数据,是不是正确的?
世界-> 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对象...然后我们可以将其datafy以获取引用内容作为数据结构,并重新进入Clojure数据世界。

如果你还没有尝试REBL,我强烈建议你试试看——这真的会使其更清晰(至少,我希望会这样)。
+1投票

nav是一个通用的导航框架。给定一个“容器”(集合)和该容器中的某个选择,导航到该选择。对于容器,k是有意义的(如二维数组),对于某些选择,它是不必要的,选择值就足够了。作为一个通用框架,两者都提供给你,因为你可能需要两者来进行选择。

关于输入和输出的具体内容,这是一个完全基于上下文的问题。nav是一个适合当前目的的工具,没有特定的目标,就无法具体说明。通常你既是nav的调用者也是实现者(通过实例元数据),最重要的是它们需要匹配。

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

我曾以为在了我拥有的东西中添加datafy/nav的支持,就会在REBL中添加支持。是吗?因为为此,我假设应该有一个“深入挖掘”的约定,比如,当你评估某些表达式时,datafy被调用并且结果在内容窗格中渲染,如果这是REBL可以选入的已知类型(如映射或向量),则在内容窗格中选一个索引并向前导航则会调用nav于已datafy的表达式,其中“k”是索引,“v”是值,然后这个结果将成为新的表达式,并重复这个过程。

我可以看出datafy/nav可以非常通用,基本可以按照你自己的约定在其内部使用,但在REBL的上下文中,是否有需要遵循的约定呢?

再次感谢!

编辑
我实际上在REBL中尝试了这一点,看起来是这样的

1. 内容窗格调用datafy于选中表达式
2. 向前导航调用nav,其中“coll”是1中的datafy返回值
3. “k”是内容窗格选中的“idx”
4. “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在哈希映射上的默认行为是只返回值v,即(get coll k),因为这正是在此上下文中调用nav的方式)。
...