请在 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),它将产生一些我们称之为geo-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 的实现,其中 `coll` 在 `nav` 的实现中从不使用:https://github.com/seancorfield/next-jdbc/blob/1b93d3a04ba033bb627e493df28f32330dfeb2e5/src/next/jdbc/result_set.clj#L643-L664

这让我更加困惑 :(
by
是真的。在这种情况下,可以使用键和值来找到“新的 Thing”,因为外键关系是基于键和值(可能是模式选项),而不是它所在的数据行。

因此,nav 可能会或可能不会关心值、键,甚至整个集合 :)
在导航过程中,通常将所需的元数据存储在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 coll数据化。并且使用正常的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 返回一个不是数据的东西,但我没有这样的东西,仅仅是因为需要而创造一个看起来很奇怪。"

如果你已经有了数据,从 nav 中返回它完全是可以的。但这里我认为你想要返回一个 "as-url" 对象,无论它的形式是什么。
评论者
>> 你应该首先数据化 URL 以获取数据,然后进入该数据(就在这里找到你的集合)。

当你提到“nav进入数据”时,我会感到困惑,因为收集可以进入,例如获取、get-in、第一个、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对象...我们可以使用datafy获取引用内容作为数据结构,并返回到Clojure数据世界。

如果你还没有尝试REBL,我强烈建议你试试——它真的会让这一切更清晰(或者至少,我希望它会)。
+1
by

nav是一个通用的导航框架。给定一个“容器”(coll)和在容器中的某些选择,将“导航”到该选择。对于容器,k有意义(如地图),对于某些容器,这是不必要的,选择值本身就足够了。作为一个通用框架,两者都提供给你,你可能需要两者来进行选择。

关于输入和输出的具体内容,这完全取决于上下文。nav是一个适用于当前目的的工具,没有特定的目标,很难说清楚。您通常是nav的调用者和实现者(通过实例元数据),重要的是它们相匹配。

在REBL中,nav用于浏览操作,您在左侧窗格中选择了某个内容,然后“浏览”进入它。其他使用可能做不同的事情(序列化对象图等)。

我以为在REBL中加入对datafy/nav的支持,也会在REBL内添加对它的支持。这不是吗?因为为此,我假设应该有一个“深入挖掘”的惯例,例如,当您评估某个表达式时,datafy会被调用,结果在内容窗格中显示,如果它是一个可以由REBL选择的已知类型,例如映射或向量,那么选择集合中的索引并将nav向前调用就会在datafied表达式上调用nav,`k`是索引,`v`是值,然后这个结果将成为新的表达式,并且它会重复。

我可以看出datafy/nav可以非常通用,基本上可以按照您自己的约定在其内部使用,但在REBL的上下文中,是否存在一个惯例可以遵循?

再次感谢!

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

1. 内容窗格调用datafy于选择的表达式
2. 前进nav调用nav,其中`coll`是来自第1步的datafy的返回值
3. 并且`k`是内容窗格中选择的`idx`
4. 并且`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在hash map上的默认行为只是返回值v,即(get coll k),这就是你在这个上下文中调用nav的方式)。
...