请在 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 的值。

通过使用datafy从《Thing》世界转换到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的实现中从未使用过collhttps://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))

这将返回另一件事(即任意可datafy的Object),这是通过导航到 "v" 获得的。

在这个意义上,"v" 应该是一个超链接,它实际上不是一个集合,这就是为什么我们导航到超链接指向的内容,这样会返回另一件事,我们以后也可以将其datafy,并且可能进一步导航。

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

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

在这个阶段,根据URL的datafy实现方式,我们可以假设我们得到了以下结果

{: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) ...

因此,在这一阶段,我们将一个URLdatafy为一个Clojure集合。并使用正常的Clojure集合函数深入其中。当我们到达另一个Navigable值时,我们对其调用nav以获取下一页面。

我只是有一点困惑。如果你看我的例子,我假设nav返回一个datafied结果。但Sean提到它不应该这样做。但在我的例子中这甚至没有意义。我从URL开始。我可以导航到这个URL以获取页面,但还没有coll上下文。而且我可以让nav返回的东西不是已经data的数据,但没有这样的事情,仅仅是因为看起来很奇怪。

这里我假设`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数据化以获取数据,然后导航到该数据(这就是你的coll)。

“我可以通过nav进入那个URL,以获取页面”

不,你应该首先将URL数据化以获取数据,然后导航到该数据(这就是你的coll)。
“我可以有一个nav返回的不是已经数据化的事物,但我没有这样的东西,只是为了制造一个而制造一个似乎很奇怪。”
如果您已经有数据,直接从nav返回它是完全正常的。但这里我认为您想要返回一个"as-url"对象,无论其形式如何。

当你说进入数据时我感到困惑,因为集合可以被导航进入,例如get、get-in、first、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 vote
by

nav是一个通用的导航框架。给定一个“容器”(集合)和一些在该容器中的选择,导航到那个选择。对于容器,k是有意义的(如映射),而对于某些容器,它可能是多余的,选择值就足够了。作为一个通用框架,两者都提供给你,因为你可能需要两者来做选择。

关于输入和输出的具体内容,这是一个完全取决于上下文的答案。nav 是一种适合当前目的的工具,不知道一个特定目标就无法具体化。你通常是 nav 的调用者和实现者(通过实例元数据),重要的是它们是一致的。

在 REBL 中,nav 用于执行浏览操作,在你左边的面板中选中某项后,你可以进入它进行浏览。其他用法可能做不同的事情(序列化对象图,等等)。

我原本以为向我的产品添加 datafy/nav 的支持会让它在 REBL 中也得到支持。是这样吗?因为为此,我认为应该有一个“钻入”的惯例,比如当你在内容面板中评估某个表达式时,datafy 会在它身上调用,并将其渲染在内容面板中,如果是 REBL 可以选择为映射或向量等已知类型,选择内容面板中的索引 idx 并向前导航就会调用 nav,其中 k 为 idx,v 为 val,然后这个结果会成为新的 expr,并重复这个过程。

我可以看到 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 的合理使用?
在你的例子中不需要nav: https://github.com/seancorfield/datafy-nav-example/blob/master/src/datafy_test/alt.clj

这会产生相同的结果,而不需要实现nav(因为nav在哈希表上的默认行为只是返回值v(即(get coll k),因为这是你在这种环境中调用nav的方式))。
...