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哈希表。

myThingdatafydescription-hash-map

现在,我们可以使用 get 来遍历这个散列映射: (get description-hash-map :foo),它将产生一个我们称之为 foo-val 的值。

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

这就是 nav 介入的地方。对于任何产生 foo-val 值的 (get description-hash-map :foo),我们可以通过调用 (nav description-hash-map :foo foo-val) 来返回到 Thing 世界中对等的点,并将在该世界产生一个新的对象。

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

myThingdatafydescription-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 功能

我确实查看过 next.jdbc 的实现,但 `coll` 永远没有被用在 `nav` 的实现中:https://github.com/seancorfield/next-jdbc/blob/1b93d3a04ba033bb627e493df28f32330dfeb2e5/src/next/jdbc/result_set.clj#L643-L664

这让我更加困惑 :)
确实如此。在这种情况下,可以使用键和值来找到“新事物”,因为外键关系是基于键和值的(以及可能的模式选项),而不是它被发现的数据行。

所以导航可能或不关注值、键,甚至整个集合:)
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) ...

在此基础上,根据数据fy 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进入该数据(这就是你的集合)。

"我本来可以让我 nav 返回不是数据的事物,但没有这样的东西,而且只是为了装样子而编造一个似乎很奇怪。"

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

当你说“nav进入数据”时,我感到困惑,因为集合可以被导航进入,例如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有很好(比如map)对于些则不必要,选择值本身就足够。作为一个通用框架,两者都提供,因为你可能需要两者来完成选择。

至于输入和输出的确切内容,这是一个完全情境化的答案。nav是一个适合当前目的的工具,不知道特定的目标,就难以具体说明。你通常是nav的调用者(通过实例元数据)和实现者,重要的是它们应该匹配。

在REBL中,nav用于支持浏览动作,当你左面板中选择了一些内容,并且“浏览”到它时。其他用途可能做不同的事情(序列化对象图,等等)。

by
我曾经以为在我想拥有的某样东西中添加对datafy/nav的支持会使其在REBL中也得到支持。但这不是真的吗?因为为了这个,我假设应该有一种“钻入”的约定,比如说,当你评估一个表达式时,datafy会被调用,并在内容窗口中呈现结果,如果它是一个REBL可以选中的已知类型,例如映射或向量,那么在被选中的集合的索引中选择,然后前进nav会调用datafy的expr上的nav,其中k是索引,v是值,并且这个结果将变成新的expr,然后重复。

我可以看到datafy/nav可以非常通用,基本上可以在自己的约定中随意使用,但在REBL的上下文中,是否有一个遵循的约定?

再次感谢!
by
实际上,我在REBL上试过这个,看起来像这样

1. 内容窗口对选定的表达式调用datafy
2. nav前调用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,因为nav在hash映射上的默认行为仅仅是返回值v(这是因为你如何在这个上下文中调用nav))。
...