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

欢迎!有关本站的工作方式,请参阅<a href="https://ask.clojure.org/index.php/about">关于页面了解更多信息。

+8
协议
重新标记

clojure.protocols/nav函数的签名是

[coll k v]

我不懂coll(集合)、k(键)和v(值)分别代表什么。请 someone解释一下?能否给出一个带有实际参数调用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 的作用。对于任何 (get description-hash-map :foo) 产生 foo-val 的值,我们可以通过调用 (nav description-hash-map :foo foo-val) 来回到 Thing 世界中的等效位置,这将产生 Thing 世界中的新对象。

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

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 的实现,并且 collnav 的实现中从未使用过:https://github.com/seancorfield/next-jdbc/blob/1b93d3a04ba033bb627e493df28f32330dfeb2e5/src/next/jdbc/result_set.clj#L643-L664

这让我更加困惑了 :)
by
确实。在这种情况下,可以通过键和值找到“新事物”——因为外键关系基于键和值(以及可能的模式选项),而不是数据行。

所以 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 返回的不是已经数据化的东西,但我没有这样的事情,仅仅因为这样去做似乎有点奇怪。

我在这里假设`nav`可以返回数据或可数据化的对象,这并不重要。并且默认会调用`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`返回一个数据化的结果."

不,应该返回一个对象。

"我可以在那个URL中通过`nav`进入,以获取页面内容."

不,你应该首先将URL数据化,以获取数据,然后通过`nav`进入该数据(这就是你的集合)。

"而且我可以让`nav`返回非数据化的东西,但我没有这样做,随便那样做似乎很奇怪."

如果你已经有数据,那么从`nav`返回它是完全可以的。但在这里,我认为你应该返回一个“as-url”对象,无论这个对象是什么形式。
by
> 你应该首先将URL数据化,以获取数据,然后进入该数据(这就是你的集合)。

当你说导航到数据时,我便感到困惑,因为集合可以被导航,例如什么 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 对象... 我们可以从该引用的内容获取数据结构,并重新进入 Clojure 数据世界。

如果您还没有尝试 REBL,强烈建议您尝试一下 - 这真的会让这更清晰(至少我希望它会)。
+1投票

nav 是一个通用的框架,用于导航。给定一个“容器”(coll)和该容器中的某些选择,导航到该选择。对于容器,如 k(例如映射),在没有选择值的情况下是多余的。作为一个通用框架,两者都提供了,你可能需要两者来完成选择。

关于输入和输出的具体内容,这是一个完全基于特定情境的答案。nav是一个针对特定目的而设计的小工具,如果不了解特定目标,就无法做具体说明。你通常是nav的调用者和实现者(通过实例元数据),而重要的是确保它们一致。

在REBL中,nav用于辅助浏览操作,即当你左窗格中选定了某个对象,并浏览进入时。其他用途可能做不同的事情(序列化对象图等)。

来自
我有一种印象,即在拥有的某些事物中添加对datafy/nav的支持会将其添加到REBL中。对吗?因为为此,我假定应该有一个用于“深入挖掘”的惯例,例如,当你评估某个表达式时,datafy会对其调用,并在内容页面上渲染结果。如果它是一个可以通过REBL选择的对象(如地图或向量)的类型,那么选择集合中的索引并向前导航就会在datafy后的表达式中调用nav,其中`k`是索引,`v`是值,然后这个结果就会成为新的表达式,并重复进行。

我可以理解datafy/nav可以非常通用,基本上可以根据自己的惯例在其内部使用,但在REBL的语境中,是否有需要遵循的惯例呢?

再次表示感谢!
来自
编辑
实际上,我已经尝试用REBL做了这件事,看起来是:

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

基于这些和此处所有的答案,我总结出这个片段https://gist.github.com/didibus/2a2a62d365f93d55db4fb27f46ecef89似乎是合理使用datafy/nav的一个例子?
在你的示例中不需要nav: https://github.com/seancorfield/datafy-nav-example/blob/master/src/datafy_test/alt.clj

这在不实现nav的情况下产生完全相同的结果(因为nav在哈希映射上的默认行为只是返回值v(因为这是在这种情况下调用nav的方式))。
...