在haskell中有没有一种方法可以隐式地将一个类型强制转换为另一个 Package 类型?

x33g5p2x  于 2022-11-14  发布在  其他
关注(0)|答案(2)|浏览(127)

我正在用Haskell编写一个简单的函数,作为我的大学作业--该函数应该取整数n,并生成一个ASCII三角形,最下面一行的宽度为2*n + 1。
这是我的实现:

module Triangle where

data TArg = CallArg Int | RecursiveArg (Int, Int)
triangle :: TArg -> String

triangle (CallArg k) = triangle (RecursiveArg (k, k))
triangle (RecursiveArg (0, _)) = ""
triangle (RecursiveArg (k, q)) = triangle (RecursiveArg (k-1, q+1)) ++ spaces ++ stars ++ spaces ++ ['\n'] where
  spaces = replicate q ' '
  stars = replicate (k*2 - 1) '*'

现在,虽然它工作-我不喜欢在调用函数时指定变量的必要性,而宁愿有某种强制或不同的方式来指定两个可能的变量。
先谢谢你了

kqqjbcuj

kqqjbcuj1#

看起来你试图用一个调用 Package 器来建立一个递归函数,因为递归调用需要第二个参数。我不认为你把参数封装在一个数据结构中的方法是一个好方法。一个更常见的方法是创建两个函数,一个是从定义外部调用的外部函数,另一个是递归调用的内部函数。通过使用作用域,你可以对外界隐藏递归的内部函数。2为了实现这一点,我将重写你的代码如下

module Triangle where

triangle :: Int -> String
triangle k = triangle' k k where
  triangle' :: Int -> Int -> String
  triangle' 0 _ = ""
  triangle' k q = 
    triangle' (k - 1) (q + 1) ++ spaces ++ stars ++ spaces ++ "\n" where
      spaces = replicate q ' '
      stars  = replicate (k * 2 - 1) '*'
2mbi3lxu

2mbi3lxu2#

Andrew Ray的答案是,如果编写一个函数,需要在其递归形式中使用与初始调用不同的调用签名,通常会采取什么样的做法。
但是为了补充这个答案,如果triangle必须像OP中所示的那样存在,你会怎么做?1
如果你总是需要调用xtriangle (CallArg x),并且希望你可以直接调用triangle x,只需创建你自己的 Package 函数:

triangle' x = triangle (CallArg x)

-- or
triangle' =  triangle . CallArg

现在,原始的triangle可以按照自己喜欢的方式使用它的签名(TArg -> String),并且你可以用你希望它拥有的签名(Int -> String)调用一个函数。每个人都赢了。而且 Package 器是微不足道的,所以即使在纯字符计数的基础上,如果你需要调用triangle'超过几次,使用 Package 器也会更短。
如果您希望避免调用与triangle不同的名称所带来的非常小的开销,并且这是一个更实际的程序,其中triangle是从不同的模块导入的内容,则可以定义自己的 Package 器模块:

module MyTriangle ( triangle )
where

import qualified Original.Triangle as OT

triangle = OT.triangle . OT.TArg

现在代码的其余部分只需要将 importimport Original.Triangle ( triangle )更改为import MyTriangle ( triangle ),并且调用triangle的所有地方(如果值得这么做的话,可以假设有很多地方)都可以使用您喜欢的名称签名。
概括:任何时候,当你想多次使用一个函数时,它的签名看起来比你实际需要的要复杂,因为它的额外结构和/或值可以从你想给予它的东西中机械地派生出来,你可以简单地创建一个 Package 器来做转换。你不需要语言来“隐式地强制”任何东西;你可以明确地说(但只能在一个地方)你想要它如何“强制”,给予它一个名字,然后仅仅通过使用这个名字就得到你想要的行为。2这样你得到简洁的代码,避免了冗余,所有的事情都是明确的。
(The Andrew Ray的回答中所展示的技术的核心实际上就是这种“翻译 Package 器”的想法,如果外部没有人需要调用除了 Package 器之外的任何东西,那么也只是将原始函数移动为纯粹的内部细节)
1比如说,因为它是一个第三方函数,你只是调用它,和/或它实际上有时会被调用,与数据结构的情况,或任何原因。

相关问题