简体   繁体   English

如何将Clojure中的整数列表传递给Frege函数?

[英]How to pass a list of integers from Clojure to a Frege function?

Inspired by the previous question what is the easiest way to pass a list of integers from java to a frege function? 受上一个问题的启发,将整数列表从java传递给frege函数的最简单方法是什么? and a comment in the answers by @Ingo, I tried @Ingo对答案的评论,我试过了

(Foo/myfregefunction (java.util.List. [1,2,3,4]))

but get (ctor = constructor): 但得到(ctor = constructor):

CompilerException java.lang.IllegalArgumentException: No matching ctor found for interface java.util.List

Any ideas? 有任何想法吗? At least java.util.List didn't yield a ClassCastException; 至少java.util.List没有产生ClassCastException; does this mean this is on the right track? 这是否意味着这是正确的轨道?

I can send Frege pretty much any Java collection type from Clojure, see Converting Clojure data structures to Java collections . 我可以从Clojure发送几乎任何Java集合类型的Frege,请参阅将Clojure数据结构转换为Java集合

BTW, using plain (Foo/myfregefunction [1,2,3,4]) instead yields ClassCastException clojure.lang.PersistentVector cannot be cast to free.runtime.Lazy , to which @Ingo points out, “A clojure list is not a frege list.” Similar response when casting as java.util.ArrayList . 顺便说(Foo/myfregefunction [1,2,3,4]) ,使用plain (Foo/myfregefunction [1,2,3,4])而不是产生ClassCastException clojure.lang.PersistentVector cannot be cast to free.runtime.Lazy ,@ Ingo指出,“一个clojure列表不是frege list。“作为java.util.ArrayList转换时的类似响应。

On the Frege side, the code is something like 在弗雷格方面,代码就像

module Foo where

myfregefunction :: [Int] -> Int
-- do something with the list here

Ok, not knowing Clojure, but from the link you gave I take it you need to give the name of an instantiable class (ie java.util.ArraList ) since java.util.List is just an interface and so cannot be constructed. 好的,不知道Clojure,但是从你给出的链接我得到它你需要给出一个可实例化的类的名称(即java.util.ArraList ),因为java.util.List只是一个接口,因此无法构造。

For the Frege side, which is the consumer in this case, it suffices to assume the interface. 对于弗雷格方面,在这种情况下是消费者,它就足以假设接口。

The whole thing gets a bit complicated, since Frege knows that java lists are mutable. 整个事情变得有点复杂,因为弗雷格知道java列表是可变的。 This means that there cannot exist a pure function 这意味着不存在纯函数

∀ s a. Mutable s (List a) → [a]

and every attempt to write such a function in a pure language must fail and will be rejected by the compiler. 并且每次尝试以纯语言编写这样的函数都必须失败并且将被编译器拒绝。

Instead, what we need is a ST action to wrap the pure part (in this case, your function myfregefunction ). 相反,我们需要的是一个ST动作来包装纯部分(在这种情况下,你的函数myfregefunction )。 ST is the monad that makes it possible to deal with mutable data. ST是monad,可以处理可变数据。 This would go like this: 这将是这样的:

import Java.Util(List, Iterator)   -- java types we need

fromClojure !list = 
    List.iterator list >>= _.toList >>= pure . myfregefunction

From clojure, you can now call something like (forgive me if I get the clojure syntax wrong (edits welcome)): 从clojure,你现在可以调用类似的东西(如果我的clojure语法错误(编辑欢迎),请原谅我):

(frege.prelude.PreludeBase$TST/run (Foo/fromClojure (java.util.ArrayList. [1,2,3,4])))

This interfacing via Java has two disadvantages, IMHO. 这种通过Java的接口有两个缺点,恕我直言。 For one, we introduce mutability, which the Frege compiler doesn't allow us to ignore, so the interface gets more complicated. 首先,我们引入了可变性,Frege编译器不允许我们忽略它,因此接口变得更加复杂。 And in addition, list data will be duplicated. 此外,列表数据将被复制。 I don't know how Clojure is doing it, but on the Frege side, at least, there is this code that goes over the iterator and collects the data into a Frege list. 我不知道Clojure是如何做到的,但至少在Frege方面,这个代码遍及迭代器并将数据收集到Frege列表中。

So a better way would be to make Frege aware of what a clojure.lang.PersistentVector is and work directly on the clojure data in Frege. 因此,更好的方法是让Frege知道clojure.lang.PersistentVector是什么,并直接处理Frege中的clojure数据。 I know of someone who has done it this way with clojure persistent hash maps, so I guess it should be possible to do the same for lists. 我知道有人用clojure持久性哈希映射这样做了,所以我想应该可以为列表做同样的事情。

(At this point I cannot but point out how valuable it would be to contribute a well thought-out Clojure/Frege interface library!) (此时我不得不指出贡献一个经过深思熟虑的Clojure / Frege接口库是多么有价值!)

Edit: As the self-answer from @0dB suggests, he's about to implement the superior solution mentioned in the previous paragraphs. 编辑:正如@ 0dB的自我回答所示,他即将实施前面段落中提到的优秀解决方案。 I encourage everyone to support this noble undertaking with upvotes. 我鼓励大家用赞成票来支持这项崇高的事业。

A third way would be to construct the Frege list directly in Clojure. 第三种方法是直接在Clojure中构建Frege列表。

Based on the answer by @Ingo, 根据@Ingo的回答,

a better way would be to make Frege aware of what a clojure.lang.PersistentVector is and work directly on the clojure data in Frege. 更好的方法是让Frege知道clojure.lang.PersistentVector是什么,并直接在Frege的clojure数据上工作。

and comments thereto as well as the solution for PersistentMap by Adam Bard, I came up with a working solution: 和评论以及Adam Bard的PersistentMap解决方案,我提出了一个有效的解决方案:

module foo.Foo where

[EDIT] As Ingo points out, being an instance of ListView gives us list comprehension, head, tail, … [编辑]正如Ingo指出的那样,作为ListView的一个实例给我们列表理解,头部,尾部......

instance ListView PersistentVector

We need to annotate a Clojure class for use in Frege ( pure native basically makes the Java methods available to Frege without needing any monad to handle mutability, possible because—in general—data is immutable in Clojure, too): 我们需要注释一个用于Frege的Clojure类( pure native基本上使得Frege可以使用Java方法而不需要任何monad来处理可变性,因为 - 通常 - 数据在Clojure中也是不可变的):

data PersistentVector a = native clojure.lang.IPersistentVector where
  -- methods needed to create new instances
  pure native empty clojure.lang.PersistentVector.EMPTY :: PersistentVector a
  pure native cons :: PersistentVector a -> a -> PersistentVector a
  -- methods needed to transform instance into Frege list
  pure native valAt :: PersistentVector a -> Int -> a
  pure native length :: PersistentVector a -> Int

Now there follow some functions that are added to this data type for creating a Clojure vector from Frege list or the other way around: 现在,遵循一些添加到此数据类型的函数,用于从Frege列表创建Clojure向量,或者反过来:

  fromList :: [a] -> PersistentVector a
  fromList = fold cons empty

  toList :: PersistentVector a -> [a]
  toList pv = map pv.valAt [0..(pv.length - 1)]

Note my use of the "dot" notation; 注意我使用“点”符号; see the excellent article by @Dierk, The power of the dot . 看看@Dierk的优秀文章, 点的力量

[EDIT] For ListView (and some fun in Frege with PersistentVector ) we need to also implement uncons , null and take (sorry for the quick & dirty solutions here; I will try to fix that soon): [编辑]对于ListView (以及Frege with PersistentVector一些乐趣),我们还需要实现unconsnulltake (抱歉这里有快速和脏的解决方案;我会尽快解决这个问题):

  null :: PersistentVector a -> Bool
  null x = x.length == 0

  uncons :: PersistentVector a -> Maybe (a, PersistentVector a)
  uncons x
    | null x = Nothing
    -- quick & dirty (using fromList, toList); try to use first and rest from Clojure here
    | otherwise = Just (x.valAt 0, fromList $ drop 1 $ toList x)

  take :: Int -> PersistentVector a -> PersistentVector a
  -- quick and dirty (using fromList, toList); improve this
  take n = fromList • PreludeList.take n • toList

In my quick & dirty solution above, note the use of PreludeList.take to avoid calling take in the namespace that PersistentVector creates, and how I did not have to prefix fromList , toList , cons and empty . 在我上面的快速和肮脏的解决方案,注意使用PreludeList.take避免调用take在命名空间PersistentVector造成的,我怎么没有前缀fromListtoListconsempty

With this setup (you can leave out uncons , null and take as well as the instance declaration at the top, if you don't want to do anything with PersistentVector in Frege directly) you can now call a Frege function that takes and returns a list by wrapping it properly: 有了这个设置(你可以离开了unconsnulltake还有instance声明顶部,如果你不想用做什么PersistentVector弗雷格直接),你现在可以调用弗雷格函数,它接受并返回一个列表正确包装:

fromClojure :: PersistentVector a -> PersistentVector a
fromClojure = PersistentVector.fromList • myfregefn • PersistentVector.toList

-- sample (your function here)
myfregefn :: [a] -> [a]
myfregefn = tail

In Clojure we just call (foo.Foo/fromClojure [1 2 3 4]) and get a Clojure vector back with whatever processing myfregefn does (in this example [2 3 4] ). 在Clojure中,我们只需调用(foo.Foo/fromClojure [1 2 3 4])并使用myfregefn执行的任何处理(在此示例中为[2 3 4] )返回一个Clojure向量。 If myfregefn returns something that both Clojure and Frege understand ( String , Long , …), leave out the PersistentVector.fromList (and fix the type signature). 如果myfregefn返回Clojure和Frege都理解的内容( StringLong ,...),则省略PersistentVector.fromList (并修复类型签名)。 Try both out, tail as above for getting back a list and head for getting back, say, a Long or a String . 尝试既出, tail如上面取回一个列表, head为取回,也就是说,一个LongString

For the wrapper and for your Frege function, make sure the type signatures 'match', eg PersistentVector a matches [a] . 对于包装器和Frege函数,请确保类型签名“匹配”,例如PersistentVector a匹配[a]

Moving forward: I am doing this because I would like to port some of my Clojure programs to Frege, “a function at a time“. 前进:我这样做是因为我想把我的一些Clojure程序移植到Frege,“一次一个功能”。 I am sure I will be encountering some more complex data structures that I will have to look into, and, I am still looking into the suggestions by Ingo to improve things. 我相信我会遇到一些我需要研究的更复杂的数据结构,而且,我仍在研究Ingo提出的改进建议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM