【问题标题】:ZipWith in Typed Racket, with Multiple Lists类型化球拍中的 ZipWith,带有多个列表
【发布时间】:2016-10-20 00:19:33
【问题描述】:

我一直在使用 Typed Racket Scheme 进行一些练习(这是我使用 Scheme 的第一个月左右),我正在尝试使用类型重写 ZipWith 函数,结果证明它比我想象的要困难得多。

这是我制作并尝试转换的无类型 ZipWith,非常简单:

(define (zipwith fn . lists)
  (apply map fn lists))

这是我对打字版本的尝试。我尝试查看 mapapply 的类型以试图弄清楚一些,但到目前为止我还没有运气:

(: zip-with (All (c a b ...) (-> (-> a b ... b c) (Listof b) ... b (Listof c))))
(define (zip-with fn . lists)
  (apply (inst map c a b ... b) fn lists))

我正在使用的 Dr Racket REPL 给了我这个错误:

Type Checker: Bad arguments to function in `apply':
Domains: (-> a b ... b c) (Listof a) (Listof b) ... b
         (-> a c) (Pairof a (Listof a))
Arguments: (-> a b ... b c) (List (Listof b) ... b) *
 in: (apply (inst map c a b ... b) fn lists)

我看到它抱怨 apply 函数,但我预期的类型是否正确?我可以就如何完全翻译我的无类型版本提出一些建议。

理想情况下,我希望类型化版本的行为尽可能接近非类型化版本,因此我可以这样做(使用非类型化版本的示例输出):

> (zipwith list '(1 2 3) '(4 5 6) '(7 8 9))
'((1 4 7) (2 5 8) (3 6 9))

编辑:我确实管理过一个类似 Haskell 的版本,基本上只是按原样复制类型签名。它是:

(: zip-with2 (All (a b c) (-> (-> a b c) (Listof a) (Listof b) (Listof c))))
(define (zip-with2 fn list1 list2)
  (map fn list1 list2))

虽然调用起来比我想的要复杂(我必须在我尝试过的函数上使用instconslist)。

【问题讨论】:

  • 值得指出的是,Racket/Scheme 中的zip-with 只是map,实际上,您的实现只是将其所有参数转发给map。因此,类型签名将与map 相同。
  • @AlexisKing 谢谢,我以前没见过!你是对的,zipwith 的签名应该与map 相同,因为它采用相同的确切参数。将apply 添加到混合中会使这个事实一开始更难看到。

标签: types racket typed-racket


【解决方案1】:

正如 Alexis King 已经指出的那样,zip-with 正是球拍中的map,其类型签名应该相同。地图类型是

> (:print-type map)
(All (c a b ...)
  (case->
   (-> (-> a c) (Pairof a (Listof a)) (Pairof c (Listof c)))
   (-> (-> a b ... b c) (Listof a) (Listof b) ... b (Listof c))))

只看多列表的情况,是这样的:

(All (c a b ...)
  (-> (-> a b ... b c) (Listof a) (Listof b) ... b (Listof c)))

但是你写的类型是这样的:

(All (c a b ...)
  (-> (-> a b ... b c) (Listof b) ... b (Listof c)))

fn 函数将a 作为第一个参数,但在您编写的类型中没有对应的(Listof a) 参数。

即使map/zip-with 的通用多参数版本也需要至少一个列表才能使用,这就是为什么(Listof a) 是必要的。这也是为什么而不是

(define (zip-with fn . lists)
  (apply map fn lists))

你需要

(define (zip-with fn list1 . lists)
  (apply map fn list1 lists))

通过这两项更改,您的代码可以正常工作:

#lang typed/racket

(: zip-with : (All (c a b ...) (-> (-> a b ... b c) (Listof a) (Listof b) ... b (Listof c))))
(define (zip-with fn list1 . lists)
  (apply (inst map c a b ... b) fn list1 lists))

它甚至可以在没有inst 表单的情况下工作:

(: zip-with : (All (c a b ...) (-> (-> a b ... b c) (Listof a) (Listof b) ... b (Listof c))))
(define (zip-with fn list1 . lists)
  (apply map fn list1 lists))

因为zip-withmap 无论如何都是一样的:

(: zip-with : (All (c a b ...) (-> (-> a b ... b c) (Listof a) (Listof b) ... b (Listof c))))
(define zip-with map)

即使它们是相同的值,这样做的好处是类型看起来稍微简单一些,只有多列表的情况。

【讨论】:

  • 太棒了!这为我清除了很多东西。谢谢!
猜你喜欢
  • 2017-06-10
  • 2021-07-30
  • 1970-01-01
  • 1970-01-01
  • 2016-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-29
相关资源
最近更新 更多