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

欢迎!请查看关于页面,了解更多关于如何使用本站的信息。

+8
协议
重新标记

函数clojure.protocols/nav具有以下签名:

[coll k v]

我不明白collkv代表什么。谁能解释一下?或许能给出一个用实际参数调用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 的作用。对于任何产生某个 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 机制

我查看了 next.jdbc 的实现,并且在 `nav` 的实现中从未使用过 `coll`:https://github.com/seancorfield/next-jdbc/blob/1b93d3a04ba033bb627e493df28f32330dfeb2e5/src/next/jdbc/result_set.clj#L643-L664

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

所以 nav 可能在乎或不计较值、键,甚至整个集合 :)
特别地,coll 经常是一个存储在 nav 过程中需要用到的元数据的好地方。
好吧,所以我理解为

- `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)
      概览页面为Coll(数据化概览页面)
  概览页面)

这是正确的吗?还是实际上应该从`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)]
  概览页面)

谢谢!
我期望nav与get匹配,而不是get-in,我期望它基于数据类型(因为它基于协议)。

(注释很不好用于代码共享,所以我不打算尝试,但我会基于您的URI导航构建一个示例,稍后再回来)
"我假设`nav`返回一个数据化的结果。"

不,应该返回一个对象。

"我可以nav进入那个URL以获取页面回来。"

不,你应首先数据化URL以获取数据,然后nav进入那些数据(那就在你的Coll)。

"并且我可以让nav返回非数据化的东西,但我没有这样的工具,仅因为这个原因而构建一个工具似乎很奇怪。"

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

当你说你nav进入数据时,我会感到困惑,因为集合可以导航进去,例如,get、get-in、first、nth等可以做的那样。为了导航到数据的元素或其嵌套集合中的元素。但也可能导航到如果它们是超链接的形式,因为其余的内容需要从远程提取和/或渲染。

最初我假设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对象...我们可以将其datafy以获取引用内容作为数据结构,再回到Clojure数据世界。

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

nav是一个用于导航的通用框架。给定一个“容器”(集合)和该容器中的一些选择,将“导航”到该选择。对于集合,k有意义(例如,映射),对于另一些,则没有必要,选择值就足够了。作为一个通用框架,两者都提供了,因为你可能需要两者来进行选择。

至于输入和输出究竟是什么,这是一个完全基于情境的回答。nav是一个适合手头工作的工具,因此不知道特定的目标,就很难具体说明。你通常是nav的调用者和实现者(通过实例元数据),重要的事情是它们要匹配。

在REBL中,nav用于浏览操作,当你左侧窗格中有所选择并“浏览”到那里时。其他用途可能做不同的事情(序列化对象图,等等)。

我曾经认为,如果在我拥有的某个东西中添加对datafy/nav的支持,那么它将在REBL内部添加支持。这样对吗?因为这样,我假设应该有一个“深入挖掘”的约定,比如说,当你评估一个表达式时,可以对它调用datafy,并将结果渲染在内容面板中,如果它是一个REBL可以通过选择进入的已知类型,例如地图或向量,那么选择集合中的索引并在前进导航中调用nav,则nav将使用`k`作为索引和`v`作为值对数据化的表达式进行调用,并且这个结果将变成新的表达式,并以此类推。

我可以看出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的方式)。
...