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

欢迎!请参阅 关于页面,了解有关如何操作的一些更多信息。

+8
协议
重标记

clojure.protocols/nav 函数的签名如下

[coll k v]

我不知道 collkv 将是什么。请解释一下?也许可以提供在具有实际参数的 nav 调用时调用 nav 的示例?

例如,coll 是什么?它是否应该是先前的 datafy 调用的结果?因此,coll 就是我们要导航进入的内容,还是 v 呢?

我假设 k 是进入的必要信息。

现在有 v,所以我又不确定这是否意味着这是我们要导航进入的值?如果是这样,那么 coll 又是什么呢?如果不是,我不明白它应该是什么。难道值不就是我们希望 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 值的 get 操作,我们可以通过调用 (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 的实现,但在 nav 的实现中 never 用过 collhttps://github.com/seancorfield/next-jdbc/blob/1b93d3a04ba033bb627e493df28f32330dfeb2e5/src/next/jdbc/result_set.clj#L643-L664

这更让我感到困惑。
commented by
确实。在这种情况下,可以通过只有一个键和一个值来找到“新的 Thing”——因为外键关系是基于键和值的(以及可能的模式选项),而不是它在其中找到的数据行。

所以导航可能或可能不会关心值、键,甚至整个集合 :)
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`,不论什么情况,但如果它已经是数据,`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)

谢谢!
作者
我本以为`nav`会匹配`get`,而不是`get-in`,并且我预期它将基于数据类型(因为它基于协议)。

(评论不适合分享代码,因此我不会尝试,但我将基于您的URI导航构建一个示例,稍后回来)
作者
"我假定`nav`返回一个数据化后的结果。"

不,应该返回一个对象。

"我可以nav进入这个URL,获取页面回退内容。"

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

"并且我可以让nav返回一个非数据化的对象,但我没有这样的东西,仅仅因此发明一个似乎很奇怪。"

如果你已经有了数据,从nav返回它是完全可以的。但在这里我觉得你想要返回一个“as-url”对象,无论它的形式如何。
作者
> 应首先数据化URL以获取数据,然后nav进入该数据(这就是你的coll。)

当你说要将mouseover调用的数据,我觉得有些困惑,因为集合是可以导航的,比如get,get-in,first,nth等等。为了导航到数据中的元素或嵌套的集合。但即使元素可以被导航,如果它们是超链接的形式,其余内容需要远程获取或渲染。

一开始我以为nav两者都做,并且将结果数据化了。但现在看起来nav似乎只做后者?

如果nav只做后者,那么对于它来说将会是许多用例的不作为,对吗?就像一个身份函数。比如在不需要获取或渲染任何内容,但只需将它们转换为数据的情况下。比如你有一个嵌套在另一个对象中的对象?

对此,我相信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是合理的(如maps),对于某些情况,它是多余的,并且选择值是足够的。作为一个通用框架,两者都提供了你可能需要同时对它们进行选择的情况。

关于输入和输出的具体内容,这是一个完全根据上下文来回答的问题。nav是一种适用于当前目的的工具,没有特定目标,就无法提供具体的答案。通常情况下,你是nav的调用者(通过实例元数据)和实现者,重要的是它们必须匹配。

在REBL中,nav用于支持浏览动作,当你在左侧面板中选择某个内容并“浏览”它时。其他用途可能执行不同的操作(序列化对象图等)。

by
我一直以为在不属于自己的东西中添加datafy/nav的支持,将会在REBL内部添加对该功能的支持。这不是吗?因为为此,我假设应该有一个“深入挖掘”的约定,例如,当你在一个表达式中进行评估,并且datafy被调用并显示在内容面板中时,如果你选择了一个已知类型,如映射或向量,那么选择集合中的一个索引然后前进nav将会调用带有k为索引,v为值的datafied表达式的nav,并将这个结果作为新的表达式,然后重复此过程。

我看懂了datafy/nav可以非常通用,基本上可以根据自己的约定来使用它,但是在REBL的上下文中,有什么约定可以遵循吗?

再次感谢!
by
编辑 by
实际上,我已经尝试了这种方法与REBL一起使用,看起来像这样

1. 内容面板对选定的表达式调用datafy
2. 前进nav调用nav,其中coll是第1步返回的datafy
3. 然后`k`是从内容面板中选择的`idx`
4. 然后`v`是从内容面板中选择的`val`

根据这一点以及这里的所有答案,我总结出一个这样的gist 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(即(get coll k),因为你就是这样在这个上下文中调用nav的)。
...