在Haskell中使用防护比使用模式进行递归功能更好吗?

如何解决在Haskell中使用防护比使用模式进行递归功能更好吗?

| 我只是想知道我在Haskell中布置的递归函数。使用防护比使用模式进行递归功能通常更好吗? 我只是不确定最佳布局是什么,但我确实知道在定义如下函数时模式会更好:
units :: Int -> String

units 0 = \"zero\"
units 1 = \"one\"
相对于
units n
    | n == 0 = \"zero\"
    | n == 1 = \"one\"
我只是不确定,到底是相同还是不同。 只是不太确定术语:我使用的是这样的东西:
f y [] = [] 
f y (x:xs) 
    | y == 0 = ...... 
    | otherwise = ...... 
还是会更好?
f y [] = [] 
f 0 (x:xs) = 
f y (x:xs) =
    

解决方法

我的一般经验法则是这样的: 当防护罩很简单地通过“ѭ4”检验时,请使用模式匹配。 使用递归时,通常会检查基本情况。因此,如果您的基本情况是简单的“ 4”格检查,请使用模式匹配。 因此,我通常会这样做:
map f [] = []
map f (x:xs) = f x : map f xs
而不是这样(
null
只是检查列表是否为空。基本上是
== []
):
map f xs | null xs   = []
         | otherwise = f (head xs) : map f (tail xs)
模式匹配旨在使您的生活更轻松,恕我直言,因此最终您应该做对您有意义的事情。如果您与一个小组一起工作,请对小组有意义。 [更新] 对于您的特殊情况,我会执行以下操作:
f _ []      = []
f 0 _       = ...
f y (x:xs)  = ...
模式匹配(如守卫)从上到下下降,在与输入匹配的第一个定义处停止。我使用下划线符号表示对于第一个模式匹配,我不在乎
y
参数是什么,对于第二个模式匹配,我不在乎list参数是什么(尽管在该计算中使用列表,则不应使用下划线)。由于它仍然是相当简单的类似于“ 4”字样的检查,因此我个人坚持使用模式匹配。 但是我认为这是个人喜好问题;您的代码是完全可读且正确的。如果我没记错的话,则在编译代码时,防护和模式匹配最终都会变成case语句。     ,一个简单的规则 如果要递归数据结构,请使用模式匹配 如果递归条件比较复杂,请使用防护措施。 讨论区 从根本上讲,这取决于您希望进行递归保护的测试。如果这是对数据类型的结构的测试,请使用模式匹配,因为它比冗余的相等测试更有效。 对于您的示例,整数上的模式匹配显然更清洁,更高效:
units 0 = \"zero\"
units 1 = \"one\"
对于任何数据类型的递归调用也是如此,您可以通过数据的形状区分大小写。 现在,如果您具有更复杂的逻辑条件,那么警卫就有意义。     ,对此并没有严格的规定,这就是为什么您得到的答案有些朦胧的原因。有些决定很容易,例如在ѭ14上进行模式匹配而不是用ѭ15进行保护,或者天堂禁止
f xs | length xs == 0 = ...
,这在多种方面都非常糟糕。但是,当没有引人注目的实际问题时,请使用使代码更清晰的任何一种。 作为示例,请考虑以下功能(实际上并没有做任何有用的工作,仅用作说明):
f1 _ [] = [] 
f1 0 (x:xs) = [[x],xs]
f1 y (x:xs) = [x] : f1 (y - 1) xs

f2 _ [] = []
f2 y (x:xs) | y == 0    = calc 1 : f2 (- x) xs
            | otherwise = calc (1 / y) : f2 (y * x) xs
  where calc z = x * ...
在《 18》中,单独的模式强调了递归有两个基本情况。在ѭ19中,警卫队强调,0只是某些计算的特殊情况(其中大部分由
calc
完成,在in警卫两个分支共享的
where
子句中定义),并且不会改变计算的结构。     ,@Dan是正确的:这基本上是个人喜好,不会影响所生成的代码。该模块:
module Test where

units :: Int -> String
units 0 = \"zero\"
units 1 = \"one\"

unitGuarded :: Int -> String
unitGuarded n
  | n == 0 = \"zero\"
  | n == 1 = \"one\"
产生了以下核心:
Test.units =
  \\ (ds_dkU :: GHC.Types.Int) ->
    case ds_dkU of _ { GHC.Types.I# ds1_dkV ->
    case ds1_dkV of _ {
      __DEFAULT -> Test.units3;
      0 -> Test.unitGuarded2;
      1 -> Test.unitGuarded1
    }
    }

Test.unitGuarded =
  \\ (n_abw :: GHC.Types.Int) ->
    case n_abw of _ { GHC.Types.I# x_ald ->
    case x_ald of _ {
      __DEFAULT -> Test.unitGuarded3;
      0 -> Test.unitGuarded2;
      1 -> Test.unitGuarded1
    }
    }
完全相同,不同的默认情况除外,这两种情况都是模式匹配错误。 GHC甚至为匹配的案例共享了字符串。     ,到目前为止的答案没有提到模式匹配的优点,这对我来说最重要:安全实现全部功能的能力。 在进行模式匹配时,您可以安全地访问对象的内部结构,而不必担心该对象是其他东西。万一您忘记了某些模式,编译器会警告您(不幸的是,默认情况下,此警告在GHC中处于关闭状态)。 例如,编写此代码时:
map f xs | null xs   = []
         | otherwise = f (head xs) : map f (tail xs)
您被迫使用非总计函数
head
tail
,从而冒着程序生命的危险。如果您在保护条件上犯了一个错误,编译器将无济于事。 另一方面,如果在模式匹配中出错,则编译器会根据错误的严重程度为您提供错误或警告。 一些例子:
-- compiles,crashes in runtime
map f xs | not (null xs)   = []
         | otherwise = f (head xs) : map f (tail xs)

-- does not have any way to compile
map f (h:t) = []
map f [] = f h : map f t


-- does not give any warnings
map f xs = f (head xs) : map f (tail xs)

-- can give a warning of non-exhaustive pattern match
map f (h:t) = f h : map f t
    

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-