簡體   English   中英

鏈接Haskell函數的數據類型

[英]Chaining Haskell Functions in data types

可以說我有以下內容:

data FuncAndValue v res = FuncAndValue (v -> res) v

chain :: (res -> new_res) -> FuncAndValue v res -> FuncAndValue v new_res
chain new_f (FuncAndValue old_f v) = FuncAndValue (new_f . old_f) v  

GHC是否可以通過內聯將函數new_fold_f成單個函數?

基本上,以數據類型存儲函數無論如何都會抑制優化。

我希望GHC能夠輕松地將函數鏈組合成一個(也就是說,我的結構上的“總和”不涉及重復調用表示(+)的thunk,而只是內聯(+)所以它像for循環一樣運行。我希望將函數存儲在數據類型中,然后稍后訪問它們不會抑制它。

GHC是否可以通過內聯將函數new_fold_f成單個函數?

是的,如果沒有介入的FuncAndValue可以做同樣的FuncAndValue 當然,需要提供功能的展開,或者無論如何也不會有任何內聯的機會。 但是如果有可能的話,將函數包裝在FuncAndValue就沒什么區別了。

但是讓我們問一下GHC本身。 首先是類型和非常簡單的chain

module FuncAndValue where

data FuncAndValue v res = FuncAndValue (v -> res) v

infixr 7 `chain`

chain :: (res -> new_res) -> FuncAndValue v res -> FuncAndValue v new_res
chain new_f (FuncAndValue old_f v) = FuncAndValue (new_f . old_f) v

apply :: FuncAndValue v res -> res
apply (FuncAndValue f x) = f x

trivia :: FuncAndValue Int (Int,Int)
trivia = FuncAndValue (\x -> (2*x - 1, 3*x + 2)) 1

composed :: FuncAndValue Int Int
composed = chain (uncurry (+)) trivia

和(有趣的部分)我們得到的triviacomposed的核心:

FuncAndValue.trivia1 =
  \ (x_af2 :: GHC.Types.Int) ->
    (case x_af2 of _ { GHC.Types.I# y_agp ->
     GHC.Types.I# (GHC.Prim.-# (GHC.Prim.*# 2 y_agp) 1)
     },
     case x_af2 of _ { GHC.Types.I# y_agp ->
     GHC.Types.I# (GHC.Prim.+# (GHC.Prim.*# 3 y_agp) 2)
     })

FuncAndValue.composed2 =
  \ (x_agg :: GHC.Types.Int) ->
    case x_agg of _ { GHC.Types.I# y_agp ->
    GHC.Types.I#
      (GHC.Prim.+#
         (GHC.Prim.-# (GHC.Prim.*# 2 y_agp) 1)
         (GHC.Prim.+# (GHC.Prim.*# 3 y_agp) 2))
    }

內聯公平,沒有(.)可見。 來自trivia的兩個case已經加入,因此我們只有一個composed 除非有人教GHC足夠的代數來簡化\\x -> (2*x-1) + (3*x+2)\\x -> 5*x + 1 ,這就像你希望的那樣好。 apply composed編譯在編譯時減少到6 ,即使在單獨的模塊中也是如此。

但這簡單,讓我們給它一個更難解決的難題。

一個可以內聯版本until (目前的定義until是遞歸的,所以GHC不內聯的話),

module WWUntil where

wwUntil :: (a -> Bool) -> (a -> a) -> a -> a
wwUntil p f = recur
  where
    recur x
        | p x       = x
        | otherwise = recur (f x)

另一個簡單的功能是它自己的模塊,

collatzStep :: Int -> Int
collatzStep n
    | n .&. 1 == 0  = n `unsafeShiftR` 1
    | otherwise     = 3*n + 1

最后,堅果

module Hailstone (collatzLength, hailstone) where

import FuncAndValue
import CollatzStep
import WWUntil

data P = P {-# UNPACK #-} !Int {-# UNPACK #-} !Int

fstP :: P -> Int
fstP (P x _) = x

sndP :: P -> Int
sndP (P _ y) = y

hailstone :: Int -> FuncAndValue Int Int
hailstone n = sndP `chain` wwUntil ((== 1) . fstP) (\(P n k) -> P (collatzStep n) (k+1))
                   `chain` FuncAndValue (\x -> P x 0) n

collatzLength :: Int -> Int
collatzLength = apply . hailstone

我使用嚴格的一對幫助了嚴格性分析器。 使用香草(,) ,第二個組件將在每個步驟中添加1后取消裝箱並重新裝箱,我不能忍受這樣的浪費;)但是否則沒有相關的區別。

核心GHC的(有趣的部分)產生:

Rec {
Hailstone.$wrecur [Occ=LoopBreaker]
  :: GHC.Prim.Int#
     -> GHC.Prim.Int# -> (# GHC.Prim.Int#, GHC.Prim.Int# #)
[GblId, Arity=2, Caf=NoCafRefs, Str=DmdType LL]
Hailstone.$wrecur =
  \ (ww_sqq :: GHC.Prim.Int#) (ww1_sqr :: GHC.Prim.Int#) ->
    case ww_sqq of wild_Xm {
      __DEFAULT ->
        case GHC.Prim.word2Int#
               (GHC.Prim.and# (GHC.Prim.int2Word# wild_Xm) (__word 1))
        of _ {
          __DEFAULT ->
            Hailstone.$wrecur
              (GHC.Prim.+# (GHC.Prim.*# 3 wild_Xm) 1) (GHC.Prim.+# ww1_sqr 1);
          0 ->
            Hailstone.$wrecur
              (GHC.Prim.uncheckedIShiftRA# wild_Xm 1) (GHC.Prim.+# ww1_sqr 1)
        };
      1 -> (# 1, ww1_sqr #)
    }
end Rec }

lvl_rsz :: GHC.Types.Int -> GHC.Types.Int
[GblId, Arity=1, Caf=NoCafRefs]
lvl_rsz =
  \ (x_iog :: GHC.Types.Int) ->
    case x_iog of _ { GHC.Types.I# tpl1_B4 ->
    case Hailstone.$wrecur tpl1_B4 0 of _ { (# _, ww2_sqH #) ->
    GHC.Types.I# ww2_sqH
    }
    }

而這正是你在沒有FuncAndValue情況下FuncAndValue 一切都很好地勾勒出一個美麗的緊湊循環。

基本上,以數據類型存儲函數無論如何都會抑制優化。

如果將函數包裝在足夠的圖層下,是的。 但它與其他價值觀相同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM