【问题标题】:Change record values in Haskell在 Haskell 中更改记录值
【发布时间】:2016-06-28 20:43:34
【问题描述】:

我正在尝试使用以下代码编写 Haskell 模块:

module RectangleMover where

data Rectangle = Rectangle { xCoordinate :: Int
                           , yCoordinate :: Int
                           , width :: Int
                           , height :: Int
                           } deriving (Show)

move :: Rectangle -> Int -> Int -> Rectangle
-- Edit 1
move rec x y =
    let rec' = { xCoordinate + x
               , yCoordinate + y
               }
    return rec

要创建一个矩形,我会输入:

let rec = Rectangle 10 10 20 30

但我现在的问题是如何实现一个“移动”这个矩形的函数? 在 C# 或 Java 中,调用会是这样的:rec.move(20,20); 但是这将如何用 Haskell 编写呢?

不幸的是,这是我第一次尝试使用函数式编程语言...

编辑 1: 我在函数中添加了代码,但在“xCoordinate + x”处仍然出现解析错误...

【问题讨论】:

  • move作为参数的两个Int是什么意思?沿 x 轴和 y 轴的偏移量?还有什么?
  • 是 x 轴和 y 轴的偏移量

标签: haskell record


【解决方案1】:

鉴于这是您第一次使用 Haskell,请使用已经提到的记录更新答案。但是,对于将来在谷歌上搜索这个的人,以及如果你觉得更有野心(或为了未来的学习)的你,Haskell 有这个非常流行且功能非常强大的库,称为 lens

以下是如何使用它来设计解决问题的方法。

{-# LANGUAGE TemplateHaskell #-}
import Control.Lens

data Rectangle = Rectangle { _xCoordinate :: Int
                           , _yCoordinate :: Int
                           , _width :: Int
                           , _height :: Int
                           } deriving (Show)
makeLenses ''Rectangle

move :: Rectangle -> Int -> Int -> Rectangle
move rect dx dy= rect
                   & xCoordinate +~ dx
                   & yCoordinate +~ dy

这个解决方案一开始可能看起来并不强大,但是当您开始尝试更新嵌套记录时,我向您保证优势会变得明显。

【讨论】:

    【解决方案2】:

    您只需将偏移量添加到角落:

    move :: Rectangle -> Int -> Int -> Rectangle
    move (Rectangle x y w h) dx dy = Rectangle (x + dx) (y + dy) w h
    

    但是,使用记录语法,您可以将其写为

    move :: Rectangle -> Int -> Int -> Rectangle
    move r@(Rectangle {xCoordinate = x, yCoordinate=y}) dx dy = r {
      xCoordinate = x + dx,
      yCoordinate = y + dy }
    

    更长,但只是因为为要更新的两个字段选择了长名称。你可以进一步定义一个Point 类型:

    data Point = Point Int Int
    movePoint :: Point -> Int -> Int -> Point
    movePoint (Point x y) dx dy = Point (x + dx) (y + dy)
    
    data Rectangle = Rectangle { corner :: Point, width :: Int, height :: Int }
    move :: Rectangle -> Int -> Int -> Rectangle
    move r@(Rectangle {corner=c}) dx dy = r { corner = movePoint c dx dy }
    

    【讨论】:

    • 当我尝试使用“Point”的版本时,我在第一行得到error: Not in scope: type variable ‘x’ ...
    • 我把它改成了data Point = Point { x :: Int, y :: Int },然后出现了Duplicate type signatures for ‘move’ 。所以我将第一个移动函数重命名为movePoint,现在我得到Couldn't match expected type ‘Int’ with actual type ‘Point -> Int’ `
    • 很抱歉; trygub 已修复它。我还没有决定是否对 Point 类型使用记录语法,并且会编写记录和非记录语法的错误组合。
    【解决方案3】:

    Haskell 值是不可变的,因此您希望返回一个新的 Rectangle 并修改所需的属性。很简单,像这样使用“记录更新语法”let myNewRec = rec { yCoordinate = 20, xCoordinate = 20 }

    【讨论】:

    • 但是我可以在我的“移动”功能块中使用这个语法吗?或者这将如何写?正如你所说,我需要创建一个新矩形并从“移动”函数返回这个新矩形,对吧?
    • 我更改了上面的代码(编辑 1),但在“xCoordinate + x”处仍然出现解析错误
    • move :: Rectangle -> Int -> Int -> Rectanglemove rec x y = rec { xCoodinate = x , yCoodinate = y }。您也可以在没有记录名称的情况下执行此操作:move (Record _ _ w h) x y = Record x y w h.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多