Home Php C# Sql C C++ Javascript Python Java Go Android Git Linux Asp.net Django .net Node.js Ios Xcode Cocoa Iphone Mysql Tomcat Mongodb Bash Objective-c Scala Visual-studio Apache Elasticsearch Jar Eclipse Jquery Ruby-on-rails Ruby Rubygems Android-studio Spring Lua Sqlite Emacs Ubuntu Perl Docker Swift Amazon-web-services Svn Html Ajax Xml Java-ee Maven Intellij-idea Rvm Macos Unix Css Ipad Postgresql Css3 Json Windows-server Vue.js Typescript Oracle Hibernate Internet-explorer Github Tensorflow Laravel Symfony Redis Html5 Google-app-engine Nginx Firefox Sqlalchemy Lucene Erlang Flask Vim Solr Webview Facebook Zend-framework Virtualenv Nosql Ide Twitter Safari Flutter Bundle Phonegap Centos Sphinx Actionscript Tornado Register | Login | Edit Tags | New Questions | 繁体 | 简体


10 questions online user: 1

13
votes
answers
30 views
+10

如何在do块中有条件地绑定?

我想在do块中实现以下内容:

 如果是条件则执行n0<  -  expr0 else n0<  -  expr0'n1<  -  expr1 n2<  -  expr2返回T n0 n1 n2   

但是Haskell给出了编译错误,除非我这样做:

 如果条件则执行n0<  -  expr0 n1<  -  expr1 n2<  -  expr2返回T n0 n1 n2否则n0<  -  expr0'n1<  -  expr1 n2<  -  expr2返回T n0 n1 n2   

它看起来非常冗长,尤其是有许多共享绑定表达式。如何使其更简洁?

实际上,我正在尝试执行以下操作:

 
    
        
up vote 10 down vote accepted favorite
沙发
+100
+50

你可以“内联”条件:

  do n0<  -   if condition then expr0 else expr0' n1< -  expr1 n2<  -  expr2 return(T n0 n1 n2)  

你可能应该在 return 表达式中使用括号,所以返回(T n0 n1 n2)

然后你可以用 liftM3 :: Monad m => (a1 - > a2 - > a3 - > r) - > m a1 - > m a2 - > m a3 - > m r

  < b> liftM3  T(如果是条件,那么expr0,则为expr0')expr1 expr2   

由于Haskell是纯语言,因此评估表达式没有副作用。但是这里的 if ... then ... else 最多会评估两个表达式中的一个。 IO a 本身也没有副作用,因为它是生成 a 的“秘诀”。

EDIT :对于你的第二个例子,它更复杂。

  do n0&lt;  -  if isJust maybeVar then  Just&lt; $&gt; (f(fromJust maybeVar) ))else  pure Nothing  n1&lt;  -  expr1 n2&lt;  -  expr2 return(T n0 n1 n2) < / pre> 

所以这里我们使用 pure Nothing 在monadic上下文中“包装” Nothing ,并且 Just&lt; $&gt; 在monadic context中的值上应用 Just

或者 @ luqui 说,我们可以在这里使用 traverse ::(Traversable t,Applicative f)=&gt; (a - &gt; fb) - &gt; ta - &gt; f(tb)

 
     
			
        

我添加了更多上下文以更精确。请告诉我我做错了什么。 - sinoTrinity 2天前

@sinoTrinity:请参阅编辑。 - Willem Van Onsem 2天前

你刚刚结束了我的一天。我仍然需要更多地思考为什么你的解决方案有效,但它绝对有点像魅力! - sinoTrinity 2天前

“due to Haskell's laziness” - that's a bit of a red herring. Even if they were evaluated eagerly, that still wouldn't mean they would be executed as well. - leftaroundabout 2 days ago

@leftaroundabout:是的我同意。我重写了那一部分。 - Willem Van Onsem 2天前

+30

由于OP在评论中说“我仍然需要更多地思考你的解决方案为什么会起作用”,我认为我会添加一些解释作为补充答案。

Haskell的 if条件然后x else y 语法实际上是几乎所有命令式语言中看到的标准if / then / else语句的模拟。它更类似于条件表达式语法(在C中看作 condition?x:y ,或者在Python中看作 x,如果条件为其他y )。一旦你记住了,关于它的其他一切都会自然而然地发生。

如果条件然后在Haskell中的x else y 的表达式,而不是的声明即可。<代码> 根据 condition 是否为真,x y 不是“要做的事情”,而只是2个不同的值; 整个if / then / else表达式是一个等于 x 或等效于 y 的值(取决于条件)。

所以考虑到这一点,让我们来看看Willem Van Onsem建议的工作版本:

  do n0&lt;  -  if condition then expr0 else expr0'n1&lt;  -  expr1 n2&lt;  - expr2 return(T n0 n1 n2)  

这里的 if条件然后expr0 else expr0'完全位于单个&lt;的右侧。 - 声明。所以' 一个值的表达式,就像 expr1 expr2 一样,在以下行中。它没有说从 expr0 绑定 n0 或从 expr0'绑定 n0 ,它只是 expr0 expr0'包含if / then / else的&lt; - 语句是绑定 n0 的内容,它将无条件地绑定到 >单个值由整个if / then / else计算。

- expr0 else n0&lt; - expr0'n1&lt; - expr1 n2&lt; - expr2 return(T n0 n1 n2)

这是使用if / then / else和声明作为then和else部分。这不是仅仅“存在”一个或另一个值,而是说“做”一件事或另一件事。这不是Haskell的if / then / else的工作方式。整个if / then / else需要能够被理解为单个值的表达式。

如果我们想象将if / then / else分解为单独的,那么这应该是清楚的。声明:

  foo = do z n1&lt;  -  expr1 n2&lt;  -  expr2 return(T n0 n1 n2)z = if condition then n0&lt;  -  expr0 else n0&lt;  -  expr0'   

应该清楚,这没有任何意义。then和else部分不是独立的值表达式,它们只在do块中有意义。并且它们需要位于 foo 中的特定 do块内,以便 n0 绑定以供以后在 return(T n0)中使用n1 n2)

由于do块的语句无论如何都被转换为表达式,你可能会认为将语句作为if表达式的then / else部分应该工作。然而,将do块转换为表达式“切换”语句,因此这不起作用。例如:

  do n&lt;  -  expr rest   

等同于:

  expr&gt;&gt; =( - &gt; rest)  

如果不这样做,我将不会对此进行全面的技术解释已经理解了,但希望你能看到 n 最终与 rest 的关系比它与 expr 的关系更紧密。在语句中( rest 表示do块的全部剩余内容,在更复杂的示例中)。没有单个表达式只表示 n&lt; - expr 部分,您可以将其放在if / then / else表达式的then或else部分中。 如果您还不了解它,我将不会对此进行全面的技术解释,但希望您可以看到 n 最终与 rest 更紧密相关它与 expr 相比,它在语句中被绑定( rest 表示do块的全部剩余内容,在更复杂的例子中)。没有单个表达式只表示 n&lt; - expr 部分,您可以将其放在if / then / else表达式的then或else部分中。 如果您还不了解它,我将不会对此进行全面的技术解释,但希望您可以看到 n 最终与 rest 更紧密相关它与 expr 相比,它在语句中被绑定( rest 表示do块的全部剩余内容,在更复杂的例子中)。没有单个表达式只表示 n&lt; - expr 部分,您可以将其放在if / then / else表达式的then或else部分中。 它与 expr 相比,它在语句中被绑定( rest 表示do块的全部剩余内容,在更复杂的例子中)。没有单个表达式只表示 n&lt; - expr 部分,您可以将其放在if / then / else表达式的then或else部分中。 它与 expr 相比,它在语句中被绑定( rest 表示do块的全部剩余内容,在更复杂的例子中)。没有单个表达式只表示 n&lt; - expr 部分,您可以将其放在if / then / else表达式的then或else部分中。

很棒的解释。if / then / else是表达式,而不是语句是关键。你可以在n0上说些什么吗? - 如果isJust maybeVar那么Just <$>(f(fromJust maybeVar))其他纯粹的Nothing,我还在努力解决Just <$>和纯粹的Nothing部分? - 昨天的sinoTrinity

15
votes
answers
44 views
+10

哈斯克尔的懒惰加泰罗尼亚数字

我如何有效地生成无限的加泰罗尼亚数字列表?我现在的工作相当快,但在我看来应该有更好的方法。

  c 1 = 1 cn = sum(zipWith(*)xs(reverse xs)): xs其中xs = c(n-1)加泰罗尼亚语= map(head.c)[1 ..]   

我尝试使用 fix 代替,但是lambda对于终止计算还不够懒:

  catalan = fix(xs  - &gt; xs ++ [zipWith(*)xs(reverse xs)])  

我意识到(++)不理想

这种更好的方法存在吗?这个功能可以做得足够懒吗?n <有明确的公式

沙发
+90
+50

可以定义 加泰罗尼亚语数字 [wiki] 归纳为:

C 0 = 1 C n + 1 =(4n + 2)× C n /(n + 2)

因此我们可以将其实现为:

  catalan :: Integral i = &GT; [i] catalan = xs其中xs = 1:zipWith f [0 ..] xs fn cn = div((4 * n + 2)* cn)(n + 2)  

例如:

  Prelude&gt; 拿10加泰罗尼亚语[1,1,2,5,14,42,132,429,1430,4862]  
     
			
        

老兄,你的答案很棒... - DamiánRafaelLattenero 8月31日4:35

@DamiánRafaelLatteneroOP确实说他们想避免直接配方。:)和[0 ..]的压缩只是用succ展开,不需要创建无关的列表,我们希望通过gc处理或首先通过融合避免。所以,cats = unfoldr((n,c) - > c`seq` Just(c,(n + 1,fnc)))(0,1)。 - Ness 8月31日15:32

@WillNess:但这不是一个直接公式,因为我们不计算c为n + 1而不计算c为n :) - Willem Van Onsem 8月31日15:39

@WillemVanOnsem好吧,你知道我的意思......他们想要卡尔的答案中的算法,通过列表,而不仅仅是一个公式。 - Ness 8月31日15:47

@WillNess:老实说,我没有在问题中读到它,它说他们想避免一个明确的公式,所以像cn = div(nCr(2 * n)n)(n + 1)。你应该避免在哪里阅读其他非显性的? - Willem Van Onsem 8月31日15:52

+30

我猜你正在寻找一个使用其中一个基本递归关系的所有加泰罗尼亚数字的懒惰,无限,自我参考列表。毕竟这与Fibonacci数字有关。但如果您想要回答特定问题,那么有助于指定您所说的重现关系。我猜这是你的意思:

  cat :: Integer  - &gt; 整数cat 1 = 1 cat n = sum [cat i * cat(n  -  i)| i&lt;  -  [1 .. n  -  1]]   

如果是这样,转换为自引用表单如下所示:

  import Data.List(inits)cats :: [Integer] cats = 1:[sum(zipWith(*)pre(reverse pre))| pre&lt;  -  tail(inits cats)]   

这比fibonacci示例复杂得多,因为重复是指列表中所有先前的条目,而不仅仅是最近的固定的少数条目。使用 Data.List 中的 inits 是在每个位置获取前缀的最简单方法。我在那里使用 tail ,因为它的第一个结果是空列表,这在这里没有用。其余的是对复发关系的直接重写,我没有太多可说的。除了...

它的表现非常糟糕。我的意思是,它比我的 cat 函数的指数递归调用更好,但是有很多列表操作正在分配然后丢弃大量的内存单元。该递归关系不适合列表数据类型的递归结构。您可以探索很多方法来提高效率,但最终它们都会非常糟糕。对于这种特殊情况,如果您想要性能,那么转向封闭形式的解决方案是可行的。

谢谢你提出算法。我已经简化了一点,避免重复反转前缀(如果你感兴趣的话)。 - Ness 8月31日15:16

+30

显然,您想要的是

 &gt; cats = 1:展开(fx  - &gt; let x = sum $ zipWith(*)fx cat in Just(x,x:fx))[1]&gt; 拿10只猫[1,1,2,5,14,42,132,429,1430,4862]   

通过展开,避免重复逆转前缀(如链接的答案)状态是一个反向前缀,同时进入状态以及产生下一个元素。

我们不需要维护的非反转前缀,因为用加密数字列表本身压缩反向前缀会解决加泰罗尼亚语前缀的长度。

你确实说过你想要的避免直接公式。

17
votes
answers
52 views
+10

我们可以在Haskell中编写一个函数来测量n个字符串占用的字节数吗?

假设我有字符串

 “abc”  

,我想计算它在内存中占用的字节数。

我可以这样做:

 导入Data.Bits finiteBitSize(“abc”:: [Char])  

但是那会破坏因为 [Char] 不是该函数支持的类型。(也就是比特不是字节,但重点是绘制我正在寻找的图片)。

我的问题是:我们可以在Haskell中编写一个函数来测量n个字符串占用的字节数吗?

沙发
+40
+50

有一个名为 ghc-datasize 的Haskell包,它允许您按每个Haskell值计算占用的内存。您甚至可以在官方Hackage文档中看到如何计算字符串大小的示例:

+130

这很复杂。

让我们来谈谈GHC,特别是关于 String ,让我们假设这个东西已被完全评估,所以我们没有迭代地使用它以GC友好的方式,我们没有延迟评估并存储代表巨大数据结构的微小thunk。

在完成所有这些简化假设之后,我们需要知道一些定义。

  type String = [Char] data [a] = [] | a:[a]  -  pseudosyntax数据Char = C#Char# - 我在猜测,找不到规范来源  

现在我们要使用一些规则拇指。首先:未装箱的东西(如 Char#)通常存储在机器字中。我们生活在64位机器的世界中,所以 Char#可能是8个字节,即使它可能只使用它的底部4个字节。第二:数据构造函数是一个单词,用于说明哪个构造函数,以及指向每个字段的单词。

现在我们已经准备好了它。

空字符串是 [] ,构造函数一个字,字段没有字,所以总共一个字。

非空字符串是 c:cs ,所以一个字对于构造函数,一个单词指向 c ,一个单词指向 cs ,一个单词指向 C# constructor, Char#的一个单词。这是5个字加上我们需要的 cs < / code>。

因此,对于长度为n的 String ,我们有5 * n个单词来表示 String 的主体和一个额外的终止 [] 实际上每个字符有40个字节!Yikes。

现在您知道为什么像 Text (或者,在适当的情况下, ByteString )这样的压缩表示是如此重要。

Char的定义与GHC.Types中的定义非常相似。唯一的额外位是CTYPE注释。 - dfeuer 8月31日17:22

GHC是否以小Int值的方式预分配常见的ASCII Char值? - dfeuer 8月31日17:29

@dfeuer啊,我敢打赌!我打赌这个的原因是我发现另一个问题(我现在很难找到),与我对最大居住点的观察结果相比,在这个答案中构建的估计值太高了; 分享小Chars或许可以解释这种差异。 - Daniel Wagner 8月31日17:51

@dfeuer一些简单的测试似乎支持这个假设:readFile“foo”>> = s - > print(长度s)>> print(长度s)大约是最大驻留时间的两倍,如果foo中有'1114111'而不是它有'是的。 - Daniel Wagner 8月31日17:55

是的,ghc会预先分配ASCII字符并确保在GC期间共享。那个和小的Int技巧都是从hbc继承的东西。 - 8天前的奥古斯都

20
votes
answers
34 views
+10

如何从纯粹的函数调用不纯的函数?

我刚刚读完了“为了好大学而学习哈斯克尔!” 预订所以我的问题可能很幼稚。我不明白的是如何从纯代码中调用“不纯的”IO函数。

这是一个用C#编写的工作示例。在我们的业务逻辑中,我们根据天气计划一些行动。我们以通常的C#方式进行。

 接口IWeatherForecast {WeatherData GetWeather(Location location,DateTime timestamp); } //具体实现从DB类读取天气DbWeather:IWeatherForecast {public override WeatherData GetWeather(Location location,DateTime timestamp){...}} class WeatherFactory {public IWeatherForecast GetWeatherProvider(){... //业务逻辑独立于任何DB类MaritimeRoutePlanner {private IWeatherForecast weatherProvider = weatherFactory.GetWeatherProvider(); public bool ShouldAvoidLocation(Location location,DateTime timestamp){WeatherData weather = weatherProvider.GetWeather(location,timestamp); if(weather.Beaufort&gt; 8)返回true; else ... ...}}   

如何在Haskell中实现这个逻辑?

实际上“纯逻辑”的MaritimeRoutePlanner调用 weatherProvider.GetWeather()这是“不纯的IO”的东西。

Haskell有可能吗?你会如何在Haskell中对此进行建模? DateTime timestamp){WeatherData weather = weatherProvider.GetWeather(location,timestamp); if(weather.Beaufort&gt; 8)返回true; else ... ...}}

如何在Haskell中实现这个逻辑?

实际上“纯逻辑”的MaritimeRoutePlanner调用 weatherProvider.GetWeather()这是“不纯的IO”的东西。

Haskell有可能吗?你会如何在Haskell中对此进行建模? DateTime timestamp){WeatherData weather = weatherProvider.GetWeather(location,timestamp); if(weather.Beaufort&gt; 8)返回true; else ... ...}}

如何在Haskell中实现这个逻辑?

实际上“纯逻辑”的MaritimeRoutePlanner调用 weatherProvider.GetWeather()这是“不纯的IO”的东西。

Haskell有可能吗?你会如何在Haskell中对此进行建模? 实际上“纯逻辑”的MaritimeRoutePlanner调用 weatherProvider.GetWeather()这是“不纯的IO”的东西。

在Haskell中是否可能?你会如何在Haskell中对此进行建模? 实际上“纯逻辑”的MaritimeRoutePlanner调用 weatherProvider.GetWeather()这是“不纯的IO”的东西。

在Haskell中是否可能?你会如何在Haskell中对此进行建模?

沙发
+110

一般问题(如何从纯函数调用不纯函数)是一个FAQ。请参阅此问题及其答案:如何从不纯的方法返回纯值

以更具功能性的方式构造代码取决于与软件架构相关的任何其他主题,在某些情况下。你在写什么样的节目?REST API?智能手机应用?控制台程序?批量工作?一个加载项?

在很多情况下,你可以使用我称之为不纯净的不纯三明治

  1. 收集来自不纯来源的所有必需数据
  2. 使用该数据调用纯函数
  3. 使用纯函数的返回值做一些不纯的东西

    在Haskell中你可以这样做,因为入口点总是不纯的。这是天气决策问题的简单草图。首先定义您将使用的数据。在这里,我只包含 beaufort 值,但我认为 WeatherData 会包含更多数据(这就是我将其定义为 data 的原因)而不是 newtype )。

      data WeatherData = WeatherData {beaufort :: Int} derived(Eq,Show)  

    您现在可以将决策逻辑编写为纯函数:

      shouldAvoidLocation :: WeatherData  - &gt; Bool shouldAvoidLocation天气= beaufort天气&gt; 8   

    加载数据是一个完全具体的操作:

      readWeatherFromDb :: Location  - &gt; LocalTime  - &gt; IO WeatherData readWeatherFromDb location timestamp =  - 实现在这里......   

    这里没有明确的抽象。此函数读取数据并返回不纯的数据。这可能是 impure-pure-impure sandwich 中的第一个(不纯的)步骤。

    现在可以根据该架构构建应用程序的入口点:

      main :: IO()main = do w&lt;
         
    			
            

这里的问题是我们无法提前收集所有数据。这应该是一个潜在的位置深深地埋藏在逻辑中,并且可能是根本不应该调用航空位置 - 例如港口拒绝所有船只,所以我们甚至不需要天气数据。这就是我们需要内部数据的原因,因为业务逻辑决定了我们在每种特定情况下需要哪些数据。 - Stefan Dorn 8月29日7:48

@StefanDorn首先要做的事情是:如果出于某种原因,不可能存在纯净不纯的三明治,那么就有办法模拟纯粹的互动。 - Mark Seemann 8月29日8:13

@StefanDorn然而,这是一个明确考虑架构可以帮助的案例。我不知道你想开发的软件的细节,但我经常听到那种反驳的论点。然而,经常会发现数据很小或者无论如何都要缓存,在这种情况下,即使并不总是使用它也可能不会有问题。毕竟,您通过严格遵守按需数据来解决什么问题? - Mark Seemann 8月29日8:18

我会这样说 - 业务逻辑的算法依赖于来自IO的数据(在这种情况下是DB)。if(get IO a == something)then if(get IO b == something)then if(get IO c == something)...并且它嵌套了许多级别。每个步骤都取决于DB的一些数据。 - Stefan Dorn 8月29日10:42

@StefanDorn我理解这一点,但通常情况下,只是批量获取所有数据可能仍然更有效。在这个特殊的场合,我可能也错了。我从未对气象数据做过任何工作,但我认为数据集可能真的非常大。在这种情况下,我会按照上面的链接找到一个免费的monad。 - Mark Seemann 8月29日10:48

板凳
+60

简而言之,您不会从不纯的“功能”(又名动作)中提取数据; 你的纯函数推送到一个新的动作。

  data WeatherData = WeatherData {beaufort :: Int,...}  -  getWeather是一个纯函数 - getWeather someLocation someDate是一个动作getWeather :: Location  - &gt; 日期时间 - &gt; IO WeatherData getWeather ld = ...  -  badWeather是一个纯函数badWeather :: WeatherData  - &gt; Bool badWeather wd = beaufort wd&gt; 8  -  avoidLocation是一个纯函数 -  avoidLocation someLocation someDate是一个动作 - 我们可以简单地使用fmap将纯函数badWeather提升(或换行)成一个新动作。avoidLocation :: Location  - &gt; 日期时间 - &gt; IO Bool avoidLocation ld = fmap badWeather(getWeather ld)  

avoidLocation 实际上不会产生布尔值; 它会创建一个动作,当最终执行时,使用 badWeather 生成一个布尔值。

原始代码中的抽象也可以通过类型别名轻松包含:type WeatherSource = Location - > DateTime - > IO WeatherData。然后getWeatherProvider可能是一个IO WeatherSource,或者更好,一个纯函数,如Options - > WeatherSource,它可能会返回dbWeather或其他函数。当您向IWeatherForecast添加方法时,可以将WeatherSource设置为数据类型并向其添加字段。 - Jon Purdy 8月30日17:27

地板
+30

如果效果和纯逻辑之间的交织对于基于“三明治”的解决方案而言过于复杂,一个选项是使用发生其效果的monad参数化您的依赖关系,然后使您的逻辑多态< / em> over all monads。

例如,这里是代码的近似翻译:

  { -  #LANGUAGE ExplicitForAll# - } dataDataData = WeatherData  -  dummy type data Location = Location  - 虚拟类型数据DateTime = DateTime  - 虚拟类型newtype WeatherForecast m = WeatherForecast {getWeather :: Location  - &gt; 日期时间 - &gt; m WeatherData}  - 只是一个创建预测类型的monadic动作WeatherFactory m = m(WeatherForecast m) - 在IO monad中工作的具体工厂aWeatherFactory :: WeatherFactory IO aWeatherFactory = do putStrLn“我正在有效地分配WeatherForecast !” 返回WeatherForecast {getWeather =  _ _  - &gt; 做putStrLn“我正在连接互联网!” 返回WeatherData} newtype MaritimeRoutePlanner m = MaritimeRoutePlanner {shouldAvoidLocation :: m Bool}  - 逻辑只知道m是monad而已。makeMaritimeRoutePlanner :: forall m。Monad m =&gt; WeatherFactory m  - &gt; MaritimeRoutePlanner m makeMaritimeRoutePlanner forecastFactory = MaritimeRoutePlanner {shouldAvoidLocation = do forecast&lt;  -  forecastFactory WeatherData&lt;  -  getWeather forecast Location DateTime return False}   

WeatherForecast WeatherFactory 具有monad的类型参数,其方法具有效果。特别是, aWeatherFactory 返回一个 WeatherFactory ,它可以通过 IO 工作。

但请注意 forall makeMaritimeRoutePlanner 的签名中。它迫使逻辑在所有可能的monad上工作,这意味着它不能使用特定于任何具体 monad的功能。

使用示例:

  * Main&gt; 让planner = makeMaritimeRoutePlanner aWeatherFactory * Main&gt; shouldAvoidLocation planner我正在有效地分配WeatherForecast!我正在连接互联网!False   

将有效的依赖项作为参数(或 Reader monad的环境)传递是相对常见的。我认为,在monad上制作逻辑多态的进一步技巧不太受欢迎。最终,生活在 IO 可能太方便了,或者至少没有足够的问题来打扰“多面性的面纱”。

(当然,

还有一个mtl风格的选项,你可以在这里提供Monad m => WeatherMonad m ......这里提供了获取天气数据但不能执行任意IO的能力。这种方法对于模拟特别有用。 - dfeuer 8月29日19:14

@dfeuer是的,我得到的印象是IO中的函数记录,免费/更自由和类似MTL的方法在实践中比这种多态技巧更多地使用。我确实喜欢它,至少在概念上,因为它感觉像是IO中函数记录的某种“自然”演变。 - danidiaz 8月29日20:13

在我看来,“工厂”方法只有在有两个不同的monad时才有意义:m(WeatherForecast n),将连接到天气服务器的效果与检索预测的事件分开。 - dfeuer 8月29日20:20

0
votes
answers
36 views
+10

從Haskell類型同義詞中提取值

3

我正在爲類寫一個多米諾骨牌遊戲,無法將自己的頭包裹在自定義類型中。我有:從Haskell類型同義詞中提取值

type DomsPlayer = Hand -> Board -> (Domino,End) 
... 
playTurn :: DomsPlayer -> (Int, Hand, Board) 
playTurn hand1 board1 = (score, hand2, board2) 
    where (dom, end) = simplePlayer hand1 board1 
        board2 = resMaybe (playDom dom board1 end) 
        hand2 = remove dom hand1 
        score = scoreBoard board2 

嘗試加載這給了我的錯誤:

Dominoes.hs:43:3: error:

• Couldn't match expected type ‘(Int, Hand, Board)’ with actual type ‘Board -> (Int, b0, Board)’

• The equation(s) for ‘playTurn’ have two arguments, but its type ‘DomsPlayer -> (Int, Hand, Board)’ has only one

| 43 | playTurn hand1 board1 = (score, hand2, board2) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

Dominoes.hs:44:37: error:

• Couldn't match type ‘Hand -> Board -> (Domino, End)’ with ‘[Domino]’

Expected type: Hand Actual type: DomsPlayer

• Probable cause: ‘hand1’ is applied to too few arguments

In the first argument of ‘simplePlayer’, namely ‘hand1’

In the expression: simplePlayer hand1 board1

In a pattern binding: (dom, end) = simplePlayer hand1 board1

| 44 | where (dom, end) = simplePlayer hand1 board1 |

如何檢索DomsPlayer值是多少?

+0

它看起來像DomsPlayer的定義是一個函數的類型同義詞,而不是數據類型。 – Zpalmtree

+0

您的類型'DomsPlayer'是兩個參數('Hand'和'Board')的函數。 你的函數'playTurn'作爲一個參數,例如'DomsPlayer'函數返回一個三元組。然而,你的'playTurn'模式匹配有2個參數('hand1'和'board1')。這不適合在一起。 – mschmidt

+1

我給了一個答案,但爲了幫助你,我想知道你在做什麼。你想收到一個'''''''和'''Board'''嗎?或者你想要一個'''(Domino,End)'''?顯然你正在定義一個函數,因爲'''DomsPlayer''是函數的同義詞。 –

沙发
0
0

如果與其定義

type DomsPlayer = Hand -> Board -> (Domino,End) 

playTurn :: (Hand -> Board -> (Domino,End)) -> (Int, Hand, Board) 

更換此DomsPlayer你會看到playTurn臨危只有一個參數,而不是兩個。

我不知道你正在嘗試做的,但顯然你需要收到HandBoard作爲單獨的PARAMS:

playTurn :: Hand -> Board -> ... -> (Int, Hand, Board) 

而且我不知道,也許還傳遞的功能鍵入DomsPlayer,並將其應用於這些單獨的參數。

但這是你的錯誤。

0
votes
answers
70 views
+10

靜態鏈接二進制文件中缺少調試符號

5

我正在使用堆棧構建靜態鏈接二進制文件,並嘗試向其添加調試符號(以下內容:https://downloads.haskell.org/~ghc/master/users-guide/debug-info.html)。但是GDB報告:no debugging symbols found靜態鏈接二進制文件中缺少調試符號

我缺少什麼?

我已經加入到ghc-options.cabal file-g -rtsopts並向ld-options-static

stack install  
    --install-ghc  
    --split-objs  
    --ghc-options="-fPIC -fllvm -pgmlo opt -pgmlc llc" 

GDB被調用如下::gdb --args nodebug-exe +RTS -V0

GHC 8.2.1

所有源代碼是在這裏:我使用堆棧使用下面的命令建立https://github.com/carbolymer/haskell-missing-debug-symbols

+1

也許加上--no-strip? '--no-strip:在庫,可執行文件等中爲所有表達式禁用DWARF調試符號剝離# – Zpalmtree

+0

@Zpalmtree,沒錯。我應該看看'stack install --help' ...你可以添加它作爲答案。 – carbolymer

沙发
0
5

--no-strip防止調試信息在棧構建中被刪除。

documentation

堆棧現在支持調試和分析與DWARF信息, 使用--no條,--no庫剝離,和 --no可執行剝標誌來禁用從編譯的庫和可執行文件中刪除這些信息的默認行爲。

0
votes
answers
36 views
+10

Haskell中的「隱形」函數參數如何工作?

3

我所談論的一個例子是takeWhileHaskell中的「隱形」函數參數如何工作?

takeWhile :: (a -> Bool) -> [a] -> [a] 

用法示例

takeWhile (< 3) [1,2,3,4,1,2,3,4] == [1,2] 

從我可以告訴(< 3)成爲(a < 3)其中a是要檢查在列表中的當前項目。

這是如何在Haskell做,我會怎麼能夠移動其中a去,所以我可以做類似

takeWhile ((length a) < 4) ["aaa", "aaaaa"] 
+3

如果你正在尋找谷歌果汁:這種語法,你省略了符號中綴運算符的一個操作數,稱爲_section_。 '(<3)'表示'( x - > x <3)'和'(「abc」++)'表示'( x - >「abc」++ x)'。 –

沙发
0
9

(< 3)被稱爲「部分」,僅適用於中綴操作符。這是編寫縮寫函數的語法糖。 (< 3)相當於x -> x < 3(3 <)相當於x -> 3 < x

因此,(< 3)是返回Bool的一個參數的函數。這正是takeWhile的期望。

隨着length,你必須寫在全面的功能:

takeWhile (x -> length x < 4) ["aaa", "aaaaa"] 

或定義自己的功能,您可以咖喱:

shorterThan n x = length x < n 

takeWhile (shorterThan 4) ["aaa", "aaaaa"] 

如果你喜歡冒險,你可以寫

takeWhile ((< 4) . length) ["aaa", "aaaaa"] 

或者可能是以更可讀的方式

(??) = flip (.) 

takeWhile (length ?? (< 4)) ["aaa", "aaaaa"] 
+0

Haskell已經訓練了我很多東西來閱讀從右到左的函數組成,因此'''版本對我來說可讀性較差。 – 4castle

+0

@ 4castle這是一個作文,但不要這樣讀。考慮到 ??作爲變量的佔位符。 「東西的長度小於四」。 –

板凳
0
3

有無處可去,你要記住,在Haskell每個功能currified,所以,想在(+)功能,可以做下,想在第一類型:

(+) :: Num a => a -> a -> a 

現在我可以做我的自定義Plusone精選,plusTwo等,如此之快得益於curryfication,因爲

(+) :: Num a => a -> a -> a 

有 「隱paranthesis」,真的是這樣的:

(+) :: Num a => a -> (a -> a) 

意思給我一個號碼給你一個功能回來,所以:

plusOne :: Num a => a -> a 
plusOne = (+1) 

plusTwo :: Num a => a -> a 
plusTwo = (+2) 

你能看到什麼?你給予它只是兩個參數中的一個變換的(+)在一個新的功能,也會有相同的<>功能,您可以創建功能greaterThanTen是這樣的:在你的takeWhile (< 3)例如

greaterThanTen :: (Num a, Ord a) => a -> a 
greaterThanTen = (>10) 

所以你(< 3)是功能「小於3」,如果你「讀」所有的功能將是「拿n而小於3」

你可以在控制檯通過詢問類型:t命令

:t (< 3) 
(< 3) :: (Num a, Ord a) => a -> Bool 
+2

但請注意'(> 10)4'與'(>)10 4'完全不同。雖然currying/partial應用程序可以讓我們修正第一個參數,但section的語法允許使用'(10 <)和'(10 <)' - 這不僅僅是在工作中起作用,它還接受編譯器的特殊解析和處理。 – chi

+0

@chi非常感謝你,像你這樣的專家,我每天都會學到新東西:),就像......昨天!如果我的記憶沒有失敗,非常感謝評論和時間 –

地板
0
3

takeWhile的第一個參數是謂詞;這是您的一元函數(< 3),當參數具有數值語義並且小於3時,這是正確的。這個選項適用於每個列表成員,當謂詞首次不成立時,這個選項組成一個新列表並終止。已通過的元素組成結果列表。

你的例子幾乎是正確的,你只能將它轉換成謂詞形式,即創建一個一元函數,當參數(字符串)的長度小於4時,這是真實的。這可以通過例如

(a -> (length a) < 4) 

然後

takeWhile (a -> (length a) < 4) ["aaa", "aaaaa"] 

應該做你的期望。

0
votes
answers
45 views
+10

是用這個簽名寫一個函數的一種方法嗎?

4

我有一個函數正在執行某些特定數據類型的工作。我想知道我能不能把它變成一般人。下面是它的標誌性的通用版本:是用這個簽名寫一個函數的一種方法嗎?

f :: Monad m => ((a -> b) -> c -> d) -> (a -> m b) -> m c -> m d 

如果以上不能寫,或許更受限制的版本可以?

f2 :: Monad m => ((a -> a) -> b -> b) -> (a -> m a) -> m b -> m b 
+2

你到目前爲止嘗試過什麼?你有什麼問題?您是否嘗試過在Hoogle上搜索這些功能? –

沙发
0
2

出現問題。知道c不會告訴你哪個a s會被傳遞給(a -> b)。你要麼需要能夠列舉的a S中的宇宙或能夠與一些檢查所提供的a參數,像

(forall f. Functor f => ((a -> f b) -> c -> f d) 

在這種情況下,變得幾乎微不足道的實施F。

一般而言,您應該嘗試概括您的功能,如((a -> b) -> c -> d),以查看是否可以用鏡頭,遍歷或類似方法替換它們。

板凳
0
4

不,這是不可能的,至少沒有非終止或不安全的操作。

該論點基本上與this one類似:我們利用f來居住一種我們知道不能居住的類型。

假設存在

f :: Monad m => ((a -> b) -> c -> d) -> (a -> m b) -> m c -> m d 

專營c ~()

f :: Monad m => ((a -> b) ->() -> d) -> (a -> m b) -> m() -> m d 

因此

(g h -> f (x _ -> g x) h (return())) 
    :: Monad m => ((a -> b) -> d) -> (a -> m b) -> m d 

Speciazlize d ~ a

(g h -> f (x _ -> g x) h (return())) 
    :: Monad m => ((a -> b) -> a) -> (a -> m b) -> m a 

Speclialize m ~ Cont t

(g h -> runCont $ f (x _ -> g x) (cont . h) (return())) 
    :: ((a1 -> b) -> a) -> (a1 -> (b -> r) -> r) -> (a -> r) -> r 

採取h = const

(g -> runCont $ f (x _ -> g x) (cont . const) (return())) 
    :: ((r -> b) -> a) -> (a -> r) -> r 

因此

(g -> runCont (f (x _ -> g x) (cont . const) (return())) id) 
    :: ((r -> b) -> r) -> r 

所以,類型((r -> b) -> r) -> r有人居住,H通過庫裏霍華德同構主義,它對應於命題直覺主義邏輯的一個定理。然而,公式((A -> B) -> A) -> APeirce's law,已知這種邏輯中不可證明。

我們得到一個矛盾,因此沒有這樣的f


相比之下,類型

f2 :: Monad m => ((a -> a) -> b -> b) -> (a -> m a) -> m b -> m b 

用術語

f2 =  g h x -> x 

居住,但我懷疑這是不是你真正想要的。

0
votes
answers
39 views
+10

哈斯克爾採取第一個字符名單,並在該列表

3

鑑於年底追加:哈斯克爾採取第一個字符名單,並在該列表

mangle :: String -> String 

,我想要做的事,如:

mangle xs = head xs -- works if you change typesig to [a] -> a 

但:

mangle xs = tail xs ++ head xs -- won't work at all! 

會喜歡在列表末尾添加列表的第一個字符,切割第一個字符。

+0

'[頭XS]'將字符轉換爲字符串,這樣你就可以添加,但你的函數是不是安全。 –

+3

切勿使用'head,tail',而是使用模式匹配。 – chi

+0

@Michael Kohl:謝謝!這非常簡單。應該明白這一點。 – Madderote

沙发
0
6

一個非常直接的解決辦法是這樣的:

mangle :: String -> String 
mangle [] = [] 
mangle (x:xs) = xs ++ [x] 
0
votes
answers
85 views
+10

如何爲Haskell語言的程序製作Makefile?

-2

我是Haskell語言中的新成員,我想爲現在正在處理的haskell程序創建一個makefile。但我不知道該怎麼做。如何爲Haskell語言的程序製作Makefile?

謝謝:)

+3

我們知道你想要什麼。大。你到目前爲止有什麼? – panther

+2

我不建議。而是使用'ghc --make',或'cabal'或'stack'。如果您確實需要'make',請閱讀有關它的文檔:https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/separate_compilation.html#using-make – chi

+1

那麼你首先想想你是什麼命令在製作程序時調用自己,然後將其轉換爲Makefile。 –

沙发
0
2

幾乎沒有哈斯克爾項目使用一個Makefile,因爲這是笨重,難以閱讀,而且容易出錯。

對於簡單的一次性項目,你真的不需要任何專門的編譯系統在所有:GHC本身可以計算出模塊之間的依賴關係:

[email protected]:/tmp$ cat >> Hello.hs 
module Main where 

import Greeting 

main = putStrLn greeting 

[email protected]:/tmp$ cat >> Greeting.hs 
module Greeting where 

greeting = "Hello, World!" 

[email protected]:/tmp$ ghc Hello.hs 
[1 of 2] Compiling Greeting   (Greeting.hs, Greeting.o) 
[2 of 2] Compiling Main    (Hello.hs, Hello.o) 
Linking Hello ... 

[email protected]:/tmp$ ./Hello 
Hello, World! 

...或者乾脆

[email protected]:/tmp$ runhaskell Hello.hs 
Hello, World! 

對於更復雜的項目,絕對是圖書館,你要使用Cabal。即您需要一個.cabal文件,例如對於上述「項目」 hello.cabal。可以通過cabal工具半自動生成這樣的文件:

[email protected]:/tmp$ mkdir hello 
[email protected]:/tmp$ mv Hello.hs hello 
[email protected]:/tmp$ mv Greeting.hs hello 
[email protected]:/tmp$ cd hello/ 
[email protected]:/tmp/hello$ cabal init 
Package name? hello 
This package name is already used by another package on hackage. Do you want to choose a different name? [default: y] n 
Package version? [default: 0.1.0.0] 
Please choose a license: 
    1) GPL-2 
    2) GPL-3 
    3) LGPL-2.1 
    4) LGPL-3 
    5) AGPL-3 
    6) BSD2 
* 7) BSD3 
    8) MIT 
    9) ISC 
    10) MPL-2.0 
    11) Apache-2.0 
    12) PublicDomain 
    13) AllRightsReserved 
    14) Other (specify) 
Your choice? [default: BSD3] 12 
Author name? [default: Justus Sagemüller] 
Maintainer email? [default: [email protected]] 
Project homepage URL? 
Project synopsis? hello 
Project category: 
* 1) (none) 
    2) Codec 
    3) Concurrency 
    4) Control 
    5) Data 
    6) Database 
    7) Development 
    8) Distribution 
    9) Game 
    10) Graphics 
    11) Language 
    12) Math 
    13) Network 
    14) Sound 
    15) System 
    16) Testing 
    17) Text 
    18) Web 
    19) Other (specify) 
Your choice? [default: (none)] 17 
What does the package build: 
    1) Library 
    2) Executable 
Your choice? 2 
What is the main module of the executable: 
* 1) Main.hs (does not yet exist, but will be created) 
    2) Main.lhs (does not yet exist, but will be created) 
    3) Other (specify) 
Your choice? [default: Main.hs (does not yet exist, but will be created)] 3 
Please specify? Hello.hs 
Source directory: 
* 1) (none) 
    2) src 
    3) Other (specify) 
Your choice? [default: (none)] 
What base language is the package written in: 
* 1) Haskell2010 
    2) Haskell98 
    3) Other (specify) 
Your choice? [default: Haskell2010] 
Add informative comments to each field in the cabal file (y/n)? [default: n] 

Guessing dependencies... 
Generating Setup.hs... 
Generating ChangeLog.md... 
Generating hello.cabal... 

You may want to edit the .cabal file and add a Description field. 

此時該文件將包含:

[email protected]:/tmp/hello$ cat hello.cabal 
-- Initial hello.cabal generated by cabal init. For further documentation, 
-- see http://haskell.org/cabal/users-guide/ 

name:    hello 
version:    0.1.0.0 
synopsis:   hello 
-- description:   
license:    PublicDomain 
author:    Justus Sagemüller 
maintainer:   [email protected] 
category:   Text 
build-type:   Simple 
extra-source-files: ChangeLog.md 
cabal-version:  >=1.10 

executable hello 
    main-is:    Hello.hs 
    -- other-modules:  
    -- other-extensions:  
    build-depends:  base >=4.10 && <4.11 
    -- hs-source-dirs:  
    default-language: Haskell2010 

這不是很完整,你仍然需要添加Greeting.hs到項目:(沒有文件擴展名)

... 
executable hello 
    main-is:    Hello.hs 
    other-modules:  Greeting 
    -- other-extensions: 
... 

但僅此而已,你現在可以做

[email protected]:/tmp/hello$ cabal build 
Resolving dependencies... 
Configuring hello-0.1.0.0... 
Preprocessing executable 'hello' for hello-0.1.0.0.. 
Building executable 'hello' for hello-0.1.0.0.. 
[1 of 2] Compiling Greeting   (Greeting.hs, dist/build/hello/hello-tmp/Greeting.o) 
[2 of 2] Compiling Main    (Hello.hs, dist/build/hello/hello-tmp/Main.o) 
Linking dist/build/hello/hello ... 

或者,只需運行腳本,cabal runcabal install以使其始終可用於您的系統。