【问题标题】:Bar charts in Haskell-d3jsHaskell-d3js 中的条形图
【发布时间】:2016-05-15 19:54:03
【问题描述】:

我决定浏览 Haskell 中的 d3js 库,但它没有通过 Stackage 安装。

$ stack install d3js

Run from outside a project, using implicit global project config
Using resolver: lts-5.2 from implicit global project's config file: /home/john/.stack/global-project/stack.yaml
While constructing the BuildPlan the following exceptions were encountered:

--  Failure when adding dependencies:    
      base: needed (>=4.6 && <4.7), 4.8.2.0 found (latest applicable is 4.6.0.1)
    needed for package d3js-0.1.0.0

相反,我更感兴趣的是查看d3js-haskell 的来源。如果我可以安装该库,这可能是最简单的示例之一:条形图。

 import Control.Monad
 import qualified Data.Text as T
 import D3JS

 test :: Int -> IO ()
 test n = T.writeFile "generated.js" $ reify (box "#div1" (300,300) >>= bars n 300 (Data1D [100,20,80,60,120]))

即使是这个简单的例子,我也有很多问题。 reify 是如何工作的?我不得不在字典中查找这个词:

把(抽象的)看作是物质的或具体的东西

你知道吗?

Reify 是一个试图在抽象和真实之间架起一座桥梁的词。恰如其分地,它源自一个“真实”的祖先词——拉丁名词res,意思是“事物”。 “reify”和相关的名词“reification”都在 19 世纪中叶首次出现在英语中,尽管“reification”要早几年,一些词典认为“reify”是名词的回形。在一般使用中,这些词是指以实际或物质的方式考虑或呈现抽象概念的行为,或通过具体示例来判断某事的行为。

如上所述,d3.js 库中的 reify 函数将表示 d3.js 对象的 Haskell 实体转换为实际的 d3.js 代码。我们有具体化对象的例子吗?我们可以罚款一个:

reify (box "#div1" (300,300) >>= bars n 300 (Data1D [100,20,80,60,120]))

括号中的对象是一个可具体化的对象。源代码之旅既有启发性又令人沮丧:

-- |Instances of Reifiable can generate a JavaScript code fragment.
class Reifiable a where
    reify :: a -> Text

这是取自d3js/Type.hs 有没有具体化对象的例子?来看看d3js/reify.hs

instance Reifiable Data1D where
    reify (Data1D ps) = surround $ T.intercalate "," $ map show' ps

instance Reifiable Data2D where
    reify (Data2D ps) = surround $ T.intercalate "," $ map (\(x,y) -> T.concat ["[",show' x,",",show' y,"]"]) ps

instance Reifiable (JSFunc params r) where
    reify (JSFunc name params) = T.concat [name,"(",T.intercalate "," $ map reify params,")"]

instance Reifiable JSParam where
    reify (ParamVar name) = name
    reify (PText t) = T.concat ["\"",t,"\""]
    reify (PDouble d) = show' d
    reify (PInt d) = show' d
    reify (PFunc (FuncTxt t)) = t
    reify (PFunc (FuncExp f)) = T.concat["function(d,i){return ",reify f,";}"]
    reify (PFunc' f) = reify f
    reify (PArray vs) = T.concat ["[",T.intercalate "," $ map reify vs,"]"]
    reify (PChainValue v) = reify v

这些是可具体化类型的示例,但这些并没有告诉我们在 haskell-d3js 中图表是如何构造的?

-- | box parent (w,h) makes an SVG container in a parent element with dimension w x h.
box :: Selector ->  (Double,Double) -> St (Var' Selection)
box parent (w,h) = do
    assign
        $ ((d3Root
            >>> select parent
            >>> func "append" [PText "svg"]
            >>> width w
            >>> height h
            >>> style "background" "#eef") :: Chain () Selection)

bars :: Int -> Double -> Data1D -> Var' Selection -> St ()
bars n width ps (Var' elem) = do
    let bar_w = width / (fromIntegral n)
    v <- assign $ Val' (mkRectData bar_w ps)
    execute $
        (Val elem :: Chain () Selection)
        >>> addRect v
        >>> fill' "red"

这些示例应该有效。看起来我们致力于红条(我什至还没有看到图表)。


让我以源代码中的一些令人沮丧的脚注结束。这是来自chart.hs

-- This modules provides high-level functions for drawing common charts, such as bar charts and scatter plots.
-- Those functions also exemplify how to compose primitive functions to achieve complex drawing.
-- This module will be expanded in the near future.

【问题讨论】:

    标签: haskell d3.js data-visualization


    【解决方案1】:

    不确定您的问题是什么,但这里是如何让 d3js 演示运行:

    1. 运行这些命令:

      stack new demo
      cd demo
      cabal get d3js-0.1.0.0
      

    确保 stack.yaml 中的 resolver: 设置相对较新(例如 >= 5.0)

    1. stack.yaml 文件中将packages 节更改为:

      packages:
      - '.'
      - d3js-0.1.0.0
      
    2. 在文件d3js-0.1.0.0/d3js.cabal中修改build-depends这一行改为:

        build-depends:       base >=4.6
      

    (即省略base 的上限)

    1. demo.cabal 中,使library 节看起来像:

      library
        hs-source-dirs:      src
        exposed-modules:     Lib
        build-depends:       base >= 4.8 && < 5, d3js, text
        default-language:    Haskell2010
      
    2. 将此用于src/Lib.hs

      {-# LANGUAGE OverloadedStrings #-}
      
      module Lib
      where
      
      import Control.Monad
      import qualified Data.Text as T
      import qualified Data.Text.IO as T
      import D3JS
      
      someFunc :: IO ()
      someFunc = putStrLn "someFunc"
      
      test :: Int -> IO ()
      test n = T.writeFile "generated.js" $ reify (box "#div1" (300,300) >>= bars n 300 (Data1D [100,20,80,60,120]))
      
    3. 运行stack ghci 并运行类似test 13 的函数。查看generated.js 中的输出。

    d3js 包已经有一段时间没有更新了,它对base 包施加了过于严格的上限。事实证明,它可以使用最新的 base 进行正常编译,因此我们将 d3js 源代码作为我们自己项目的一部分并对其进行调整以使其能够编译。

    【讨论】:

    • 运行起来真是太好了。我想知道当您制作的图表不在他的示例中时会发生什么?
    • @ErikR 不妨考虑将您的构建规则补丁提交到 d3js 包中,这样人们就可以简单地 stack install 而无需进行调整。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多