javascript

git

java

python

git-remote

c++

github

django

c#

reactjs

variables

var

loops

iteration

std

c++-faq

git-checkout

generator

coroutine

spring-mvc

Haskell Thrift库在性能方面比C ++慢300倍

我正在构建一个包含两个组件的应用程序-用Haskell编写的服务器和用Qt(C ++)编写的客户端。 我正在节俭地与他们交流,我想知道为什么它这么慢。

我进行了性能测试,这是我的机器上的结果

结果

C++ server and C++ client:

Sending 100 pings                    -    13.37 ms
Transfering 1000000 size vector      -   433.58 ms
Recieved: 3906.25 kB
Transfering 100000 items from server -  1090.19 ms
Transfering 100000 items to server   -   631.98 ms

Haskell server and C++ client:

Sending 100 pings                       3959.97 ms
Transfering 1000000 size vector      - 12481.40 ms
Recieved: 3906.25 kB
Transfering 100000 items from server - 26066.80 ms
Transfering 100000 items to server   -  1805.44 ms

为什么Haskell在测试中这么慢? 如何提高性能?

这些是文件:

档案

性能节俭

namespace hs test
namespace cpp test

struct Item {
    1: optional string    name
    2: optional list<i32> coordinates
}

struct ItemPack {
    1: optional list<Item>     items
    2: optional map<i32, Item> mappers
}


service ItemStore {
    void ping()
    ItemPack getItems(1:string name, 2: i32 count) 
    bool     setItems(1: ItemPack items)

    list<i32> getVector(1: i32 count)
}

主站

{-# LANGUAGE ScopedTypeVariables #-}   
module Main where

import           Data.Int  
import           Data.Maybe (fromJust) 
import qualified Data.Vector as Vector
import qualified Data.HashMap.Strict  as HashMap
import           Network

-- Thrift libraries
import           Thrift.Server

-- Generated Thrift modules
import Performance_Types
import ItemStore_Iface
import ItemStore


i32toi :: Int32 -> Int
i32toi = fromIntegral

itoi32 :: Int -> Int32
itoi32 = fromIntegral

port :: PortNumber
port = 9090

data ItemHandler = ItemHandler

instance ItemStore_Iface ItemHandler where
    ping _                   = return () --putStrLn "ping"
    getItems _ mtname mtsize = do 
        let size = i32toi $ fromJust mtsize
            item i = Item mtname (Just $ Vector.fromList $ map itoi32 [i..100])
            items = map item [0..(size-1)]
            itemsv = Vector.fromList items 
            mappers = zip (map itoi32 [0..(size-1)]) items 
            mappersh = HashMap.fromList mappers
            itemPack = ItemPack (Just itemsv) (Just mappersh)
        putStrLn "getItems"
        return itemPack

    setItems _ _             = do putStrLn "setItems"
                                  return True

    getVector _ mtsize       = do putStrLn "getVector"
                                  let size = i32toi $ fromJust mtsize
                                  return $ Vector.generate size itoi32

main :: IO ()
main = do
    _ <- runBasicServer ItemHandler process port 
    putStrLn "Server stopped"

ItemStore_client.cpp

#include <iostream>
#include <chrono>
#include "gen-cpp/ItemStore.h"

#include <transport/TSocket.h>
#include <transport/TBufferTransports.h>
#include <protocol/TBinaryProtocol.h>

using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;

using namespace test;
using namespace std;

#define TIME_INIT  std::chrono::_V2::steady_clock::time_point start, stop; \
                   std::chrono::duration<long long int, std::ratio<1ll, 1000000000ll> > duration;
#define TIME_START start = std::chrono::steady_clock::now(); 
#define TIME_END   duration = std::chrono::steady_clock::now() - start; \
                   std::cout << chrono::duration <double, std::milli> (duration).count() << " ms" << std::endl;

int main(int argc, char **argv) {

    boost::shared_ptr<TSocket> socket(new TSocket("localhost", 9090));
    boost::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
    boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));

    ItemStoreClient server(protocol);
    transport->open();

    TIME_INIT

    long pings = 100;
    cout << "Sending " << pings << " pings" << endl;
    TIME_START
    for(auto i = 0 ; i< pings ; ++i)
        server.ping();
    TIME_END


    long vectorSize = 1000000;

    cout << "Transfering " << vectorSize << " size vector" << endl;
    std::vector<int> v;
    TIME_START
    server.getVector(v, vectorSize);
    TIME_END
    cout << "Recieved: " << v.size()*sizeof(int) / 1024.0 << " kB" << endl;


    long itemsSize = 100000;

    cout << "Transfering " << itemsSize << " items from server" << endl;
    ItemPack items;
    TIME_START
    server.getItems(items, "test", itemsSize);
    TIME_END


    cout << "Transfering " << itemsSize << " items to server" << endl;
    TIME_START
    server.setItems(items);
    TIME_END

    transport->close();

    return 0;
}

ItemStore_server.cpp

#include "gen-cpp/ItemStore.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>

#include <map>
#include <vector>

using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;


using namespace test;
using boost::shared_ptr;

class ItemStoreHandler : virtual public ItemStoreIf {
  public:
    ItemStoreHandler() {
    }

    void ping() {
        // printf("ping\n");
    }

    void getItems(ItemPack& _return, const std::string& name, const int32_t count) {

        std::vector <Item> items;
        std::map<int, Item> mappers;

        for(auto i = 0 ; i < count ; ++i){
            std::vector<int> coordinates;
            for(auto c = i ; c< 100 ; ++c)
                coordinates.push_back(c);

            Item item;
            item.__set_name(name);
            item.__set_coordinates(coordinates);

            items.push_back(item);
            mappers[i] = item;
        }

        _return.__set_items(items);
        _return.__set_mappers(mappers);
        printf("getItems\n");
    }

    bool setItems(const ItemPack& items) {
        printf("setItems\n");
        return true;
    }

    void getVector(std::vector<int32_t> & _return, const int32_t count) {
        for(auto i = 0 ; i < count ; ++i)
            _return.push_back(i);
        printf("getVector\n");
    }
};

int main(int argc, char **argv) {
    int port = 9090;
    shared_ptr<ItemStoreHandler> handler(new ItemStoreHandler());
    shared_ptr<TProcessor> processor(new ItemStoreProcessor(handler));
    shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
    shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
    shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());

    TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
    server.serve();
    return 0;
}

生成文件

GEN_SRC := gen-cpp/ItemStore.cpp gen-cpp/performance_constants.cpp gen-cpp/performance_types.cpp
GEN_OBJ := $(patsubst %.cpp,%.o, $(GEN_SRC))

THRIFT_DIR := /usr/local/include/thrift
BOOST_DIR := /usr/local/include

INC := -I$(THRIFT_DIR) -I$(BOOST_DIR)

.PHONY: all clean

all:   ItemStore_server ItemStore_client

%.o: %.cpp
    $(CXX) --std=c++11 -Wall -DHAVE_INTTYPES_H -DHAVE_NETINET_IN_H $(INC) -c $< -o $@

ItemStore_server: ItemStore_server.o $(GEN_OBJ) 
    $(CXX) $^ -o $@ -L/usr/local/lib -lthrift -DHAVE_INTTYPES_H -DHAVE_NETINET_IN_H

ItemStore_client: ItemStore_client.o $(GEN_OBJ)
    $(CXX) $^ -o $@ -L/usr/local/lib -lthrift -DHAVE_INTTYPES_H -DHAVE_NETINET_IN_H

clean:
    $(RM) *.o ItemStore_server ItemStore_client

编译并运行

我生成文件(使用此处的thrift 0.9):

$ thrift --gen cpp performance.thrift
$ thrift --gen hs performance.thrift

编译

$ make
$ ghc Main.hs gen-hs/ItemStore_Client.hs gen-hs/ItemStore.hs gen-hs/ItemStore_Iface.hs gen-hs/Performance_Consts.hs gen-hs/Performance_Types.hs -Wall -O2

运行Haskell测试:

$ ./Main& 
$ ./ItemStore_client

运行C ++测试:

$ ./ItemStore_server&
$ ./ItemStore_client

记住在每次测试后都要杀死服务器

更新资料

编辑了getVector方法以使用getItems代替Vector.fromList,但仍然无效

更新2

由于@MdxBhmt的建议,我测试了getItems函数,如下所示:

getItems _ mtname mtsize = do let size = i32toi $! fromJust mtsize
                                  item i = Item mtname (Just $!  Vector.enumFromN (i::Int32) (100- (fromIntegral i)))
                                  itemsv = Vector.map item  $ Vector.enumFromN 0  (size-1)
                                  itemPack = ItemPack (Just itemsv) Nothing 
                              putStrLn "getItems"
                              return itemPack

在我的原始实现的基础上,这是严格的,并且改进了Vector的生成,相对于其替代方法:

getItems _ mtname mtsize = do let size = i32toi $ fromJust mtsize
                                  item i = Item mtname (Just $ Vector.fromList $ map itoi32 [i..100])
                                  items = map item [0..(size-1)]
                                  itemsv = Vector.fromList items 
                                  itemPack = ItemPack (Just itemsv) Nothing
                              putStrLn "getItems"
                              return itemPack

请注意,没有发送HashMap。 第一个版本的时间为12338.2毫秒,第二个版本为11698.7毫秒,没有加速:(

更新3

我向Thrift Jira报告了一个问题

abhinav的Update 4

这是完全不科学的,但是使用带有Thrift 0.9.2的GHC 7.8.3和@MdxBhmt的版本getItems,可以大大减少差异。

C++ server and C++ client:

Sending 100 pings:                     8.56 ms
Transferring 1000000 size vector:      137.97 ms
Recieved:                              3906.25 kB
Transferring 100000 items from server: 467.78 ms
Transferring 100000 items to server:   207.59 ms

Haskell server and C++ client:

Sending 100 pings:                     24.95 ms
Recieved:                              3906.25 kB
Transferring 1000000 size vector:      378.60 ms
Transferring 100000 items from server: 233.74 ms
Transferring 100000 items to server:   913.07 ms

执行了多次执行,每次都重新启动服务器。 结果是可重现的。

请注意,原始问题的源代码(使用@MdxBhmt的getItems实现)将无法原样编译。 必须进行以下更改:

getItems _ mtname mtsize = do let size = i32toi $! fromJust mtsize
                                  item i = Item mtname (Just $!  Vector.enumFromN (i::Int32) (100- (fromIntegral i)))
                                  itemsv = Vector.map item  $ Vector.enumFromN 0  (size-1)
                                  itemPack = ItemPack (Just itemsv) Nothing 
                              putStrLn "getItems"
                              return itemPack

getVector _ mtsize       = do putStrLn "getVector"
                              let size = i32toi $ fromJust mtsize
                              return $ Vector.generate size itoi32
trans by 2020-08-12T08:06:03Z

haskell-函数式编程能最好/最坏地解决哪些问题?

我经常听到函数式编程解决了许多在过程式/命令式编程中很难解决的问题。 但是我也听说过,过程编程自然很擅长解决其他一些问题。

在我开始阅读有关Haskell的书并深入研究函数式编程之前,我至少想要一个基本的概念,以了解我可以真正将其用于什么(书中的示例之外)。 那么,函数式编程最擅长哪些方面? 它不太适合哪些问题?

更新资料

到目前为止,我对此有一些很好的答案。 我迫不及待想现在开始学习Haskell-我只需要等待直到我掌握C :)

函数式编程之所以如此出色的原因:

  • 非常简洁明了-它可以用简短,清晰的陈述表达复杂的想法。
  • 比命令性语言更容易验证-在系统安全至关重要的情况下很好。
  • 功能的纯正和数据的不变性使并发编程更加合理。
  • 非常适合编写脚本和编写编译器(不过我很想知道为什么)。
  • 与数学相关的问题可以简单而精美地解决。

函数式编程遇到的问题:

  • 值得商:的:Web应用程序(尽管我想这取决于应用程序)。
  • 桌面应用程序(尽管它可能取决于语言,但是F#会很好,不是吗?)。
  • 性能至关重要的任何事物,例如游戏引擎。
  • 任何涉及大量程序状态的事物。
trans by 2020-08-12T00:51:59Z

haskell-了解涉及GHCi让绑定的thunk的不同行为

我一直在玩Simon Marlow的书中有关并行和并发的一些示例在Haskell中进行编程,偶然发现了一个我不太了解的有趣行为。 这真的是关于我试图了解GHC的一些内部运作方式。

假设我在REPL中执行以下操作:

λ» let x = 1 + 2 :: Int
λ» let z = (x,x)
λ» :sprint x
x = _
λ» :sprint z
z = (_,_)
λ» seq x ()
()
λ» :sprint z
z = (3,3)

好的,这几乎是我所期望的,只是z已经获得WHNF的评估。 让我们编写一个类似的程序并将其放入文件中:

module Thunk where

import Debug.Trace

x :: Int
x = trace "add" $ 1 + 2

z :: (Int,Int)
z = (x,x)

并在GHCi中弄弄它:

λ» :sprint x
x = _
λ» :sprint z
z = _
λ» seq x ()
add
()
λ» :sprint z
z = _
λ» seq z ()
()
λ» z
(3,3)

因此,这表现有些不同:事先未对WHNF评估z。 我的问题是:

为什么在执行let z = (x,x)时在REPL中将z评估为WHNF,但在从文件中加载定义时却没有。 我的怀疑是它与模式绑定有关,但我不知道在哪里查找来进行澄清(也许我完全是完全错误的)。 我本来希望它的行为类似于文件中的示例。

任何指示或简短解释为什么会发生这种情况?

trans by 2020-08-11T20:30:47Z

地图-Haskell出现歧义-如何避免?

我在GHCI中执行以下操作:

:m + Data.Map
let map = fromList [(1, 2)]
lookup 1 map

GHCI知道地图是(Map Integer Integer)。 那么,为什么在类型清晰且可以避免的情况下在Prelude.lookup和Data.Map.lookup之间声明歧义?

<interactive>:1:0:
    Ambiguous occurrence `lookup'
    It could refer to either `Prelude.lookup', imported from Prelude
                          or `Data.Map.lookup', imported from Data.Map

> :t map
map :: Map Integer Integer
> :t Prelude.lookup
Prelude.lookup :: (Eq a) => a -> [(a, b)] -> Maybe b
> :t Data.Map.lookup
Data.Map.lookup :: (Ord k) => k -> Map k a -> Maybe a
trans by 2020-08-11T18:46:08Z

类型类-GHC Haskell当前的约束系统怎么了?

我听说,从GHC 7.6及更低版本开始,Haskell的“中断”约束系统存在一些问题。 它出什么问题了? 有没有类似的现有系统可以克服这些缺陷?

例如,edwardk和tekmo都遇到了麻烦(例如tekmo的评论)。

trans by 2020-08-11T17:35:27Z

浮点-Haskell范围和浮点数

为什么float的Haskell范围符号的行为与整数和char的行为不同?

Prelude> [1, 3 .. 10] :: [Int]
[1,3,5,7,9] 
Prelude> [1, 3 .. 10] :: [Float]
[1.0,3.0,5.0,7.0,9.0,11.0]
Prelude> ['a', 'c' .. 'f']
"ace"

如果最后一个元素接近上限,我会理解的,但这显然不是一个四舍五入的问题。

trans by 2020-08-11T05:37:56Z

scala-从哪里开始依赖类型编程?

这里有一个Idris教程,一个Agda教程以及许多其他教程风格的论文和介绍性材料,这些参考文献永无休止地涉及要学习的东西。 在所有这些中,我有点爬行,在大多数时候,我都坚持使用数学记号,而新术语突然出现而没有任何解释。 也许我的数学很烂:-)

是否有任何规范的方法来进行依赖类型编程? 就像您想学习Haskell一样,您从“自学Haskell”开始,当您想要学习Scala时,您也从Odersky的书开始,对于Ruby,您阅读了其中包含突变错误的怪异教程。 但是我不能以他们的书开始阿格达或伊德里斯的书。 他们远在我头上。 我尝试了Coq,并陷入了证明蠕虫的风格。 阿格达(Agda)需要大量的数学背景,而伊德里斯(Idris)好吧,现在就让我们离开吧!

我非常了解静态类型系统,我精通Scala,并且在必要时可以使用Haskell。 我了解功能范式并每天使用它,我了解代数数据类型和GADT(实际上相当顺利),最近我设法理解了Lambda多维数据集。 不过,我缺少数学和逻辑部分。

trans by 2020-08-10T22:11:14Z

oop-在纯函数式编程环境中的面向对象编程?

在功能编程(FP)上下文中使用面向对象编程(OOP)有什么优势?

我已经使用F#一段时间了,我注意到我的函数越是无状态的,就越不需要将它们作为对象的方法。 特别是,依靠类型推断使它们可在尽可能多的情况下使用具有优势。

这并不排除需要某种形式的名称空间,该名称空间与成为OOP正交。 也不鼓励使用数据结构。 实际上,FP语言的实际使用在很大程度上取决于数据结构。 如果查看在F Sharp编程/高级数据结构中实现的F#堆栈,您会发现它不是面向对象的。

在我看来,OOP与使方法作用于对象状态主要是为了使对象发生突变而紧密相关。 在不需要或不需要的纯FP上下文中。

一个实际的原因可能是能够与OOP代码进行交互,这与F#与.NET的工作方式几乎相同。 除此之外,还有什么原因吗? 在Haskell的世界中(编程更纯正的FP)有什么经验?

我会很高兴提及任何有关此问题的论文或反事实的例子。

trans by 2020-08-09T09:00:24Z

haskell-quotRem和divMod之间的区别何时有用?

从haskell报告中:

quot,rem,div和mod类 如果y为,则方法满足这些定律 非零:

quot

quot是整数除法被截断 接近零,而结果div 被截断为负无穷大。

例如:

Prelude> (-12) `quot` 5
-2
Prelude> (-12) `div` 5
-3

截断结果的方式之间的区别在哪些方面重要呢?

trans by 2020-08-09T03:19:47Z

haskell-Parsec vs Yacc / Bison / Antlr:为什么以及何时使用Parsec?

我是Haskell和Parsec的新手。 在阅读了第16章“使用真实世界的Haskell的Parsec”之后,我想到了一个问题:为什么和什么时候Parsec比其他解析器生成器(如Yacc / Bison / Antlr)更好?

我的理解是,Parsec创建了编写解析器的不错的DSL,而Haskell使其变得非常容易且富有表现力。 但是解析是一种标准/流行的技术,应该使用自己的语言,并输出到多种目标语言。 因此,什么时候应该使用Parsec而不是从Bison / Antlr生成Haskell代码?

这个问题可能不仅仅涉及技术,而且涉及行业实践领域。 从头开始编写解析器时,与Bison / Antlr或类似的东西相比,选择Haskell / Parsec有什么好处?

顺便说一句:我的问题与这一问题非常相似,但是没有得到令人满意的回答。

trans by 2020-08-09T00:34:40Z

C#-F#vs Haskell vs Lisp-学习哪种语言?

我听说过很多有关函数式编程语言的知识,并且愿意学习一种。 我想这主要是为了娱乐,但是,我希望它能提高我的编程技能。

我主要具有C#/。NET背景,所以我的第一选择是学习F#(因为.NET和Visual Studio熟悉)。 另一方面,我想知道F#是否具有Lisp宏或Haskell高阶函数之类的功能。

您能比较一下F#,Haskell和Lisp吗? 您选择的是哪一种语言?

trans by 2020-08-07T06:19:45Z

hashmap-执行者Haskell哈希结构。

我正在编写执行大量表查找的程序。 因此,当我偶然发现Data.Map(当然)以及Data.HashMapData.Hashtable时,我一直在仔细阅读Haskell文档。我不是哈希算法方面的专家,并且在检查了软件包之后,它们看起来真的很相似。 因此,我想知道:

1:有什么主要区别(如果有)?

2:在具有约4000个键值对的地图/表上进行大量查找时,哪个性能最高?

trans by 2020-08-05T10:52:25Z

共享库-如何在Haskell项目中使用DLL?

我想在Haskell项目中使用外部库RDFox。

上下文:我正在使用GHC7.10和堆栈在64位的Windows和Linux上工作。 RDFox用C ++编程。 可以使用Java和Python包装程序下载RDFox共享库(.dll,.so)。

目的:我想在Haskell的项目中重复使用RDFox(.dll,.so)中已编译的库,因此我需要为RDFox创建一个Haskell包装器。

问题:对于Haskell来说,它相对较新,我很难知道从哪里开始。 我从Haskell Wiki和StackOverflow找到了几个有关该主题的页面,但是工作流程和配置对我来说还不清楚。

问题:我想知道:

  1. 如何配置堆栈和cabal以使用外部库,以在Windows或Linux(不同的机器,相同的存储库)上构建。
  2. 如何配置GHCi在此外部库上进行交互式测试。
  3. 将Python包装器翻译为Haskell是最好的方法吗? 我想避免对RDFox C ++代码的分析。
trans by 2020-08-05T09:48:25Z

学习Haskell以学习

我读过一些问题,例如Scala与Haskell讨论了两种语言的优缺点或需要学习的语言,但是我已经知道我想学习Scala。 我是uni的Java程序员,现在主要使用PHP。

我想学习Scala,因为它看起来像是对个人项目Java的改进,并且我还想学习一种功能语言,以提高我作为程序员的知识。

我想知道学习Haskell作为函数式编程的入门是一个好主意,因为它纯粹是函数式的,因此我会适当地学习它,而不是在Scala中使用函数式的零散而又不知道为什么吗?

我也想将Haskell用于个人项目等,因为它看起来很棒,但是我并没有真正看到它在现实世界中的许多应用,似乎更多地用于学术方面,因此想学习它以获得功能上的理解然后继续 到斯卡拉。

trans by 2020-08-05T09:04:05Z

haskell-功能编程范例是否有可视化建模语言或样式?

UML是旨在以OO语言编写的软件建模的标准,并且与Java紧密相连。 仍然可以将其用于建模要以功能编程范例编写的软件吗? 给定嵌入式视觉元素,哪些图将变得有用?

是否有针对函数式编程的建模语言,更具体地说是Haskell? 您会建议使用哪些工具来绘制图表?

由OP编辑,2009年9月2日:

我正在寻找的是代码中最直观,最轻巧的表示。 易于理解的图表,可视化模型不一定针对其他程序员。 我将很快在Haskell中开发游戏,但是由于该项目是为我的毕业总结工作而设计的,因此我需要介绍所提议解决方案的某种形式化形式。 我想知道是否有等效于UML + Java的标准,但Haskell除外。我应该只坚持情节提要,书面说明,非形式化的图表(一些类似于流程图的浅图像),非形式化的用例描述吗?

由jcolebrand编辑,2012年6月21日:

请注意,提问者最初想要一个视觉隐喻,现在已经有三年了,我们正在寻找更多/更好的工具。 最初的答案都没有真正解决“视觉隐喻设计工具”的概念,因此……这就是新的赏金所希望提供的。

trans by 2020-08-05T04:28:24Z

在Has中定义枚举的更好方法

我想要一个数据类型来表示可以通过特定名称寻址的有限整数集。 我认为最好的方法是使用枚举。

但是,有一个小问题。 我知道的定义枚举的唯一方法是这样的:

data MyDataType = Foo | Bar | Baz

instance Enum MyDataType 
 toEnum 0 = Foo
 toEnum 1 = Bar
 toEnum 2 = Baz

 fromEnum Foo = 0
 fromEnum Bar = 1
 fromEnum Baz = 2 

请注意,我必须重复两次相同的配对-一次是在定义整数到枚举的映射时,另一次是在定义枚举到整数的映射时。

有办法避免这种重复吗?

trans by 2020-08-04T22:54:57Z

性能-为什么差异列表比常规串联更有效?

我目前正在通过“在线学习Haskell”一书来研究自己的方式,并进入了一个章节,作者在其中解释一些列表串联可能效率低下:例如

((((a ++ b) ++ c) ++ d) ++ e) ++ f

据说效率低下。 作者提出的解决方案是使用“差异列表”定义为

newtype DiffList a = DiffList {getDiffList :: [a] -> [a] }

instance Monoid (DiffList a) where
    mempty = DiffList (\xs -> [] ++ xs)
    (DiffList f) `mappend` (DiffList g) = DiffList (\xs -> f (g xs))

我在努力理解为什么DiffList在某些情况下比简单的串联具有更高的计算效率。 有人可以简单地向我解释为什么上面的示例如此低效,而DiffList用什么方式解决了这个问题?

trans by 2020-08-04T22:19:54Z

功能隐私和单元测试具有

您如何处理Haskell中的功能可见性和单元测试?

如果在模块中导出每个函数,以便单元测试可以访问它们,则可能会冒其他人调用不应在公共API中使用的函数的风险。

我想到使用{-# LANGUAGE CPP #-},然后用#ifdef围绕出口:

{-# LANGUAGE CPP #-}

module SomeModule
#ifndef TESTING
( export1
, export2
)
#endif
where

有没有更好的办法?

trans by 2020-08-03T10:10:38Z

lambda-如何编写以可变参数为参数的Haskell函数

我正在尝试创建一个将可变参数函数作为参数的函数,即

func :: (a -> ... -> a) -> a

我该怎么做?

我已经读过关于多元函数的信息,并且我确定Oleg已经做到了,但是我迷失了尝试以可变参数函数作为参数对函数应用模式。 特别是Olegs方法似乎仅适用于glasgow扩展,并且我希望该解决方案可以在纯Haskell 98中使用(就像Text.Printf一样)。

我问的原因是我正在尝试构建一个将布尔函数作为参数并检查它是否是重言式的函数,即

isTautology :: (Bool -> ... -> Bool) -> Bool

这样就可以输入:

isTautology (\x -> x && not x)
isTautology (\x y -> x && y || not y)

我的问题是,我一直在阅读有关窍门的方法是使返回类型成为类型变量(以便它可以是结果或另一个函数),但是返回类型是固定的(布尔)。

trans by 2020-08-03T09:17:40Z

haskell-什么是谓语?

我对Haskell禁止的“强制性”类型有相当不错的直觉:即那些在->之外的类型构造函数的参数中出现forall的类型。但是,谓词是什么? 是什么使它重要? 它与“谓词”有什么关系?

trans by 2020-08-03T06:16:29Z

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 下一页 共21页