3

我正在大学学习 Haskell 的一门学科——这是我在业余时间完成的一个研讨会问题,你可能猜到我正在努力解决 Haskell 中的类型问题。我已经完成了这个问题,但没有公布答案,但我很想知道这是否是最好的方法。

这是问题中提供的 HTML 的基本表示形式

type HTML = [HTML_element]
data HTML_element
    = HTML_text String
    | HTML_font Font_tag HTML
    | HTML_p HTML
    | HTML_ul [HTML]
    | HTML_ol [HTML]
data Font_tag = Font_tag (Maybe Int) (Maybe String) (Maybe Font_color)
data Font_color
    = Colour_name String
    | Hex Int
    | RGB Int Int Int

该问题要求构建一个 Haskell 函数“num_font_tags”,该函数将计算此类 HTML 表示中 font_tag 的所有实例。接下来是我的回答,我花了整整一个小时的头撞墙,但它编译得很好,我相信这是一个解决方案:

num_font_tags :: HTML -> Int
num_font_tags [] = 0
num_font_tags (x:xs) = num_font_tag_single x + num_font_tags xs

num_font_tags_list :: [HTML] -> Int
num_font_tags_list [] = 0
num_font_tags_list (x:xs) = num_font_tags x + num_font_tags_list xs

num_font_tag_single :: HTML_element -> Int
num_font_tag_single (HTML_text _) = 0
num_font_tag_single (HTML_font _ html) = num_font_tags html
num_font_tag_single (HTML_p html) = num_font_tags html
num_font_tag_single (HTML_ul html) = num_font_tags_list html
num_font_tag_single (HTML_ol html) = num_font_tags_list html

最初我以为我只需要第一个函数,但我遇到了问题,因为它只接受 HTML 类型,但后来我需要另外两个函数 - 一个接受 [HTML] 类型,一个接受 HTML_element 类型。

这真的是我需要做这个问题的方式吗?或者我是否因为拥有 3 个功能而太过分了。可以用一个来完成吗?我的主要问题是——我是否遗漏了一些关于 Haskell 本质的东西,这会使这变得更简单?

4

2 回答 2

6

您只需要一个这样的功能,其他的可以简单地简化为

num_font_tag_single :: HTML_element -> Int

和。sum_map

你有它,你有

num_font_tags xs = sum $ map num_font_tag_single xs

num_font_tag_list xs = sum $ map num_font_tags xs
                --   = sum . map num_font_tag_single $ concat xs

您还可以从其他两个函数中派生其余函数,因为您可以轻松地从单个值获取列表,但无论如何您需要在某些时候检查 a HTML_element,因此最好num_font_tag_single建立构建的基础.

你有

num_font_tag_single (HTML_font _ html) = num_font_tags html

应该是

num_font_tag_single (HTML_font _ html) = num_font_tags html + 1
                                                        --  ^^^

或者您可以将函数简化为num_font_tag_single _ = 0.

除此之外,这基本上是唯一的方法,无论您是否命名在包含的HTML_elements resp 列表中对字体标签数量求和的函数。[HTML]sum . map num_font_tag_single分别使用。sum . map num_font_tag_single . concat主要取决于个人喜好,但由于目标是一个函数

num_font_tags :: HTML -> Int

给它一个名字(并num_font_tags . concat在递归中使用)是很自然的。

于 2013-03-29T13:41:07.367 回答
0

在 Haskell 中,每个问题都有一个简短、令人敬畏且难以理解的抽象解决方案:

{-# LANGUAGE DeriveDataTypeable #-}

import Control.Lens
import Data.Data
import Data.Data.Lens


type HTML = [HTML_element]
data HTML_element
    = HTML_text String
    | HTML_font Font_tag HTML
    | HTML_p HTML
    | HTML_ul [HTML]
    | HTML_ol [HTML]
  deriving (Data, Typeable)

data Font_tag = Font_tag (Maybe Int) (Maybe String) (Maybe Font_color)
  deriving (Data, Typeable)

data Font_color
    = Colour_name String
    | Hex Int
    | RGB Int Int Int
  deriving (Data, Typeable)

instance Plated HTML_element where
  plate = uniplate

isFont :: HTML_element -> Bool
isFont (HTML_font _ _) = True
isFont _               = False

num_font_tags :: HTML -> Int
num_font_tags = length . filter isFont . concatMap universe

num_font_tags2 :: HTML -> Int
num_font_tags2 = lengthOf $ each . parts . each . filtered isFont

These use the Control.Lens package. The second solution takes the length of a 'fold' obtained by

  1. Going through each element of the HTML list.
  2. Going through the list of HTML_element subcomponents of each HTML_element, including itself.
  3. Going through each HTML_element of every such list.
  4. Skipping the ones that aren't HTML_fonts.

The parts traversal and universe function are only available for data types that are instances of the Plated typeclass. I implemented it with uniplate, which needs data types to be instances of Data and Typeable; those can be derived with the DeriveDataTypeable language extension.

于 2013-03-29T16:37:03.250 回答