如何定义Lisp在Haskell中的应用?

如何解决如何定义Lisp在Haskell中的应用?

| 难道不应该在像Haskell这样的懒惰的语言中使用该定义来定义函数吗?
apply f [] = f
apply f (x:xs) = apply (f x) xs
它基本上是一个将给定函数应用于给定参数列表的函数,例如在Lisp中非常容易做到。 有什么解决方法吗?     

解决方法

        很难将静态类型赋予
apply
函数,因为它的类型取决于(可能是异构的)列表参数的类型。我可以想到至少有两种方法可以在Haskell中编写此函数: 使用反射 我们可以将应用程序的类型检查推迟到运行时:
import Data.Dynamic
import Data.Typeable

apply :: Dynamic -> [Dynamic] -> Dynamic
apply f []      = f
apply f (x:xs)  = apply (f `dynApp` x) xs
请注意,现在Haskell程序可能会在运行时因类型错误而失败。 通过类型类递归 使用半标准的
Text.Printf
技巧(由IIRC于augustss发明),可以将这种解决方案编码为这种样式(运动)。尽管它可能不是很有用,但仍需要一些技巧来隐藏列表中的类型。 编辑:如果不使用动态类型或hlists / existentials,我想不出一种写此方法的方法。希望看到一个例子     ,        我喜欢Sjoerd Visscher的答复,但是扩展名-尤其是
IncoherentInstances
,在这种情况下用于部分应用,可能有点令人生畏。这是不需要任何扩展的解决方案。 首先,我们定义一个函数的数据类型,该函数知道如何处理任意数量的参数。您应在此处将ѭ5读为\“参数类型\”,将
b
读为\“返回类型\”。
data ListF a b = Cons b (ListF a (a -> b))
然后,我们可以编写一些(Haskell)函数来调整这些(可变)函数。对于后奏中出现的所有功能,我都使用
F
后缀。
headF :: ListF a b -> b
headF (Cons b _) = b

mapF :: (b -> c) -> ListF a b -> ListF a c
mapF f (Cons v fs) = Cons (f v) (mapF (f.) fs)

partialApply :: ListF a b -> [a] -> ListF a b
partialApply fs          []     = fs
partialApply (Cons f fs) (x:xs) = partialApply (mapF ($x) fs) xs

apply :: ListF a b -> [a] -> b
apply f xs = headF (partialApply f xs)
例如,
sum
函数可被视为可变函数:
sumF :: Num a => ListF a a
sumF = Cons 0 (mapF (+) sumF)

sumExample = apply sumF [3,4,5]
但是,我们还希望能够处理普通函数,这些函数不一定知道如何处理任何数量的参数。那么该怎么办?好吧,像Lisp一样,我们可以在运行时引发异常。下面,我们将使用ѭ12作为非变性函数的简单示例。
f True True True  = 32
f True True False = 67
f _ _ _ = 9

tooMany = error \"too many arguments\"
tooFew  = error \"too few arguments\"
lift0 v = Cons v tooMany
lift1 f = Cons tooFew (lift0 f)
lift2 f = Cons tooFew (lift1 f)
lift3 f = Cons tooFew (lift2 f)

fF1 = lift3 f

fExample1 = apply fF1 [True,True,True]
fExample2 = apply fF1 [True,False]
fExample3 = apply (partialApply fF1 [True,False]) [False]
当然,如果您不喜欢分别定义
lift0
lift1
lift2
lift3
等的样板,则需要启用一些扩展。但是没有它们,您可以走得很远! 这是您可以概括为单个
lift
函数的方法。首先,我们定义一些标准的类型级别数字:
{-# LANGUAGE MultiParamTypeClasses,FlexibleInstances,FlexibleContexts,TypeFamilies,UndecidableInstances #-}

data Z = Z
newtype S n = S n
然后介绍要提升的typeclass。您应该将类​​型
I n a b
读取为\“
a
n
副本作为参数,然后返回ѭ6of \”的返回类型。
class Lift n a b where
    type I n a b :: *
    lift :: n -> I n a b -> ListF a b

instance Lift Z a b where
    type I Z a b = b
    lift _ b = Cons b tooMany

instance (Lift n a (a -> b),I n a (a -> b) ~ (a -> I n a b)) => Lift (S n) a b where
    type I (S n) a b = a -> I n a b
    lift (S n) f = Cons tooFew (lift n f)
这是之前使用ѭ12的示例,并使用广义提升进行了重写:
fF2 = lift (S (S (S Z))) f

fExample4 = apply fF2 [True,True]
fExample5 = apply fF2 [True,False]
fExample6 = apply (partialApply fF2 [True,False]) [False]
    ,        不,它不能。
f
f x
是不同的类型。由于haskell具有静态类型的性质,因此它无法执行任何功能。它必须具有特定类型的功能。 假设以类型
a -> b -> c
传入
f
。那么
f x
的类型为
b -> c
。但是
a -> b -> c
必须具有与
a -> b
相同的类型。因此,类型
a -> (b -> c)
的函数必须是类型
a -> b
的函数。因此
b
必须与
b -> c
相同,后者是无限类型
b -> b -> b -> ... -> c
。它不存在。 (继续用
b -> c
代替
b
)     ,        这是在GHC中执行此操作的一种方法。您将在这里和那里需要一些类型注释,以使GHC确信一切都将顺利进行。
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE IncoherentInstances #-}

class Apply f a r | f -> a r where
  apply :: f -> [a] -> r
instance Apply f a r => Apply (a -> f) a r where
  apply f (a:as) = apply (f a) as
instance Apply r a r where
  apply r _ = r

test = apply ((+) :: Int -> Int -> Int) [1::Int,2]

apply\' :: (a -> a -> a) -> [a] -> a
apply\' = apply

test\' = apply\' (+) [1,2]
    ,        此代码很好地说明了静态类型检查和动态类型检查之间的区别。使用静态类型检查,编译器无法确定
apply f
是否确实传递了
f
期望的参数,因此它会拒绝程序。简而言之,检查是在运行时完成的,然后程序可能会失败。     ,        我不确定在用F#编写代码时这将有多大帮助,但是我认为这也可以在Haskell中轻松完成:
type \'a RecFunction  = RecFunction of (\'a -> \'a RecFunction)
let rec apply (f: \'a RecFunction) (lst: \'a list) = 
    match (lst,f) with
    | ([],_) -> f
    | ((x::xs),RecFunction z) -> apply (z x) xs
在这种情况下,所讨论的\“ f \”是使用可允许递归数据类型定义的已区分联合定义的。我猜这可以用来解决提到的问题。     ,在其他一些人的帮助和输入下,我定义了一种实现此目标的方法(使用自定义列表类型),这与以前的答案有些不同。这是一个古老的问题,但是似乎仍然需要访问,因此我将添加完整的方法。 我们使用一个扩展名(GADT),其列表类型与Daniel Wagner的有点类似,但是具有标记功能类型而不是Peano编号。让我们逐段浏览代码。首先,我们设置扩展名并定义列表类型。数据类型是多态的,因此在此公式化中,参数不必具有相同的类型。
{-# LANGUAGE GADTs #-}

-- n represents function type,o represents output type
data LApp n o where
  -- no arguments applied (function and output type are the same)
  End :: LApp o o
  -- intentional similarity to ($)
  (:$) :: a -> LApp m o -> LApp (a -> m) o

infixr 5 :$ -- same as :
让我们定义一个可以接受这样的列表并将其应用于函数的函数。这里有些类型的骗术:函数的类型为
n
,仅当此类型与列表类型的
n
标记匹配时,才会编译对
listApply
的调用。通过不指定输出类型
o
,我们可以在其中留出一些自由(创建列表时,我们不必立即完全固定可应用的功能类型)。
-- the apply function
listApply :: n -> LApp n o -> o
listApply fun End = fun
listApply fun (p :$ l) = listApply (fun p) l
而已!现在,我们可以将函数应用于存储在列表类型中的参数。期望更多? :)
-- showing off the power of AppL
main = do print . listApply reverse $ \"yrruC .B lleksaH\" :$ End
          print . listApply (*) $ 1/2 :$ pi :$ End
          print . listApply ($) $ head :$ [1..] :$ End
          print $ listApply True End
不幸的是,我们被锁定在列表类型中,我们不能只转换普通列表以使用
listApply
。我怀疑这是类型检查器的根本问题(类型最终取决于列表的值),但是老实说,我不确定。
-- Can\'t do this :(
-- listApply (**) $ foldr (:$) End [2,32]
如果您对使用异构列表感到不舒服,您所要做的就是向
LApp
类型添加一个额外的参数,例如:
-- Alternative definition
-- data FList n o a where
--   Nil :: FList o o a
--   Cons :: a -> FList f o a -> FList (a -> f) o a
此处的“ 5”表示参数类型,应用的函数还必须接受所有相同类型的参数。     ,        这并不完全是您对原始问题的答案,但是我认为这可能是您的用例的答案。
pure f <*> [arg] <*> [arg2] ...
-- example
λ>pure (\\a b c -> (a*b)+c) <*> [2,4] <*> [3] <*> [1]
[7,13]
λ>pure (+) <*> [1] <*> [2]
[3]
list的适用实例比这个超级狭窄的用例要广泛得多...
λ>pure (+1) <*> [1..10]
[2,3,5,6,7,8,9,10,11]
-- Or,apply (+1) to items 1 through 10 and collect the results in a list

λ>pure (+) <*> [1..5] <*> [1..5]
[2,10]

{- The applicative instance of list gives you every possible combination of 
elements from the lists provided,so that is every possible sum of pairs 
between one and five -}

λ>pure (\\a b c -> (a*b)+c) <*> [2,4] <*> [4,3] <*> [1]
[9,17,13]
{- that\'s -  2*4+1,2*3+1,4*4+1,4*3+1
Or,I am repeating argC when I call this function twice,but a and b are 
different -}
λ>pure (\\a b c -> show (a*b) ++ c) <*> [1,2] <*> [3,4] <*> [\" look mah,other types\"]
[\"3 look mah,other types\",\"4 look mah,\"6 look mah,\"8 look mah,other types\"]
因此,确切地说,这不是一个相同的概念,但是它包含了许多组成用例,并增加了一些其他用例。     

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 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时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;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,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;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[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 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 -&gt; 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(&quot;/hires&quot;) 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&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-