list - 如何将此 if-then-else 构造更改为使用模式匹配或/和防护的构造?
问题描述
我得到了以下练习(其中一个链接在一起以漂亮地打印表格并在其中进行选择):
编写一个函数 select :: Field → Field → Table → Table,给定列名和字段值,仅从表中选择给定列中具有给定字段值的那些行。如果给定的列不存在于表中,则表应原样返回。(提示:使用函数 (!!)、elemIndex、filter 等等。)
最后得到了这个解决方案:
select :: Field -> Field -> Table -> Table
select column value table@(header:rows) =
let i = fromMaybe (-1) (elemIndex column header)
in if i == (-1) then table
else header : filter (\r -> r !! i == value) rows
虽然它的功能似乎完全正确——它有效——但有人告诉我,诸如此类的 if-then-else 结构是“糟糕的形式”,应该避免使用守卫(就像我使用 fromMaybe 使用模式匹配一样)。
我将如何通过模式匹配/防护将其更改为“更好”的样式?
解决方案
fromMaybe
在查看您的代码时,我立即看到的一个简单改进是,使用转换为 -1似乎毫无意义Nothing
,然后只需检查if
该值是否为 -1(无论使用 an 还是使用警卫都无关紧要)。为什么不Nothing
首先检查呢?
我猜你可能已经被其他语言中的类似函数以这种方式引导,如果找不到索引,则 -1 作为人工值(有时称为“哨兵值”)返回,表示“找不到元素” - 但在 Haskell 中,Nothing
这方面的沟通要好得多。
进一步Nothing
可以进行模式匹配,因此if
您可以使用case
语句而不是使用或守卫。这会将您的功能变为:
select :: Field -> Field -> Table -> Table
select column value table@(header:rows) =
case elemIndex column header of
Nothing -> table
Just i -> header : filter (\r -> r !! i == value) rows
在我看来,这比你原来的要好得多。
我仍然觉得它可以进一步改进 - 特别(!!)
是在惯用的 Haskell 代码中很少使用,因为如果索引超出范围,它就有崩溃的风险。与其他语言的数组索引运算符相比,它的效率也相当低(因为 Haskell 列表是链表,而不是数组,并且要说第 100 个元素它必须通过前 99 个元素而不是能够直接“随机使用权”)。但是,鉴于您必须在这里工作,我看不出您如何才能真正避免这种情况。
推荐阅读
- android - 应用 android:theme="@style/Theme.AppCompat" 时
在 AndroidManifest.xml 中删除 ActionBar - python - Python - 遍历列表中多个数据帧的每一行
- geopy - geopy 为 None 返回纬度和经度
- python - 我想从数据框中的数据列中删除数字和括号
- r - R 评估错误中的 read_csv 问题:必须命名第 1 列
- javascript - 在 javascript 中动态添加选择/选项不适用于 IE
- python - 需要帮助在 python 中绘制二维数组。我正在使用 Spyder(Python 3.6)
- scala - Scala 在 try 块中解析期货
- java - 运行 Spring Boot 应用程序时处理“地址已在使用中:绑定”的异常
- reasoning - GraphDB 非单调推理?