2011-10-15 9 views
42

मैं अजगर में तो जैसे वैकल्पिक तर्क को परिभाषित करने में सक्षम होने के लिए इस्तेमाल किया है:क्या हास्केल में वैकल्पिक तर्क रखने का कोई बेहतर तरीका है?

def product(a, b=2): 
    return a * b 

हास्केल डिफ़ॉल्ट तर्क नहीं है, लेकिन मैं एक हो सकता है का उपयोग करके कुछ इसी तरह प्राप्त करने में सक्षम था:

product a (Just b) = a * b 
product a Nothing = a * 2 

यदि आपके पास एकाधिक पैरामीटर से अधिक है तो यह बोझिल हो जाता है। उदाहरण के लिए, क्या मैं कुछ इस तरह करना चाहते हैं:

def multiProduct (a, b=10, c=20, d=30): 
    return a * b * c * d 

मैं बहु उत्पाद के आठ परिभाषाएँ सभी मामलों के लिए खाते में करने के लिए होगा।

इसके बजाय, मैं इस के साथ जाने का फैसला किया:

multiProduct req1 opt1 opt2 opt3 = req1 * opt1' * opt2' * opt3' 
    where opt1' = if isJust opt1 then (fromJust opt1) else 10 
    where opt2' = if isJust opt2 then (fromJust opt2) else 20 
    where opt3' = if isJust opt3 then (fromJust opt3) else 30 

यही मेरे लिए बहुत असजीला लग रहा है। क्या हैस्सेल में ऐसा करने का एक बेवकूफ तरीका है जो क्लीनर है?

+2

तो कितने मापदंडों एक समारोह * वास्तव में * लगता है? ;-) –

+0

मुझे नहीं लगता कि यह * सटीक * एक ही प्रश्न है इसलिए मैं बंद करने के लिए वोट नहीं दूंगा, लेकिन यह http://stackoverflow.com/questions/2790860/optional-arguments-in-haskell के समान ही है – MatrixFrog

+1

@MatrixFrog यह प्रश्न फ़ंक्शंस के लिए तर्कों के बारे में है। यह सवाल डेटा प्रकारों के मूल्यों के बारे में है। –

उत्तर

28

यहाँ हास्केल में अभी तक एक और तरीका है वैकल्पिक तर्क करना है:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-} 
module Optional where 

class Optional1 a b r where 
    opt1 :: (a -> b) -> a -> r 

instance Optional1 a b b where 
    opt1 = id 

instance Optional1 a b (a -> b) where 
    opt1 = const 

class Optional2 a b c r where 
    opt2 :: (a -> b -> c) -> a -> b -> r 

instance Optional2 a b c c where 
    opt2 = id 

instance (Optional1 b c r) => Optional2 a b c (a -> r) where 
    opt2 f _ b = \a -> opt1 (f a) b 

{- Optional3, Optional4, etc defined similarly -} 
फिर

{-# LANGUAGE FlexibleContexts #-} 
module Main where 
import Optional 

foo :: (Optional2 Int Char String r) => r 
foo = opt2 replicate 3 'f' 

_5 :: Int 
_5 = 5 

main = do 
    putStrLn $ foo  -- prints "fff" 
    putStrLn $ foo _5  -- prints "fffff" 
    putStrLn $ foo _5 'y' -- prints "yyyyy" 

अद्यतन: ओह, मैं स्वीकार कर लिया गया। मैं ईमानदारी से लगता है कि luqui's answer यहाँ सबसे अच्छा एक है:

  • प्रकार पढ़ने के लिए स्पष्ट है, और आसान है, यहां तक ​​कि शुरुआती
  • एक ही प्रकार की त्रुटियों के लिए
  • GHC संकेत प्रकार निष्कर्ष करने की ज़रूरत नहीं है के लिए इसके साथ
  • वैकल्पिक तर्क आदेश स्वतंत्र हैं (मैं क्या मतलब है यह देखने के लिए GHCi में opt2 replicate 3 'f' कोशिश)
+1

निश्चित रूप से आप _5 को परिभाषित करने के बजाय (5 :: Int) का उपयोग कर सकते हैं? – Max

+1

@ मैक्स: हाँ - मैं सिर्फ स्पष्टता के लिए जा रहा था। वाईएमएमवी :) – rampion

14

मैं मूल समस्या को हल करने के लिए एक बेहतर तरीका के बारे में पता नहीं है, लेकिन अपने उदाहरण अधिक संक्षेप के रूप में लिखा जा सकता है:

multiProduct req1 opt1 opt2 opt3 = req1 * opt1' * opt2' * opt3' 
    where opt1' = fromMaybe 10 opt1 
      opt2' = fromMaybe 20 opt2 
      opt3' = fromMaybe 30 opt3 
14

यहाँ an idiom from Neil Mitchell, जो endorsed by Brent Yorgey too हो रहा है है।

+0

अच्छा, लेकिन आप आंशिक आवेदन से कैसे निपटते हैं? – pat

+2

मैंने उस विचार को देखा, लेकिन आईएमओ जो भी बदतर है। प्रत्येक समारोह के लिए नए डेटा प्रकार, कोई फंक्शन करी नहीं, और वैश्विक नामस्थान को अनावश्यक रूप से दूषित ... यक। –

+9

@Goose, अगर आप "हर फंक्शन" के लिए इस तकनीक का उपयोग करने पर विचार कर रहे हैं, तो आप वास्तव में हास्केल नहीं लिख रहे हैं। यह कुछ मॉड्यूल इंटरफेस के लिए एक सभ्य तकनीक है (सीएफ। [Parsec.token] (http://hackage.haskell.org/packages/archive/parsec/3.0.0/doc/html/Text-Parsec-Token.html#t : टोकनपार्सर)), लेकिन उसके बाद आप बेवकूफ हास्केल में उतरना चाहते हैं - कई छोटे combinators, कहते हैं, 1-3 पिथी पैरामीटर (उदाहरण के लिए बूल नहीं) प्रत्येक। मुझे पास्कॉन की तरह हास्केल लिखने की आरामदायक अपील मिलती है, लेकिन आपको इसे अपना रास्ता बनाकर भाषा से अधिक संतुष्टि मिल जाएगी। – luqui

66

शायद कुछ अच्छा अंकन आंखों पर आसान होगा:

(//) :: Maybe a -> a -> a 
Just x // _ = x 
Nothing // y = y 
-- basically fromMaybe, just want to be transparent 

multiProduct req1 opt1 opt2 opt3 = req1 * (opt1 // 10) * (opt2 // 20) * (opt3 // 30) 

आप एक बार से अधिक पैरामीटर का उपयोग करना है, मैं @ पैट की विधि के साथ जा रहा सुझाव देते हैं।

संपादित 6 साल बाद

ViewPatterns के साथ आप बाईं तरफ चूक डाल सकते हैं।

{-# LANGUAGE ViewPatterns #-} 

import Data.Maybe (fromMaybe) 

def :: a -> Maybe a -> a 
def = fromMaybe 

multiProduct :: Int -> Maybe Int -> Maybe Int -> Maybe Int -> Int 
multiProduct req1 (def 10 -> opt1) (def 20 -> opt2) (def 30 -> opt3) 
    = req1 * opt1 * opt2 * opt3 
+11

मूल रूप से, '(//) = माईबे से फ़्लिप करें :-) मुझे लगता है कि आपने पर्ल के समान ऑपरेटर को चुना है। – pat

+2

मुझे यह पसंद है क्योंकि यह '||' जैसा दिखता है, बस थोड़ा सा slant :) –

+1

@pat, perl मेरी विरासत है :-) – luqui

6

जब तर्क बहुत जटिल मिलता है, एक ही समाधान एक डेटा प्रकार जूस बनाने के लिए है तर्क के लिए टी।फिर आप उस प्रकार के लिए एक डिफ़ॉल्ट कन्स्ट्रक्टर बना सकते हैं, और केवल उस फ़ंक्शन को भरें जिसे आप अपनी फ़ंक्शन कॉल में बदलना चाहते हैं।

उदाहरण:

$ runhaskell dog.hs 
Snoopy (Beagle): Ruff! 
Snoopy (Beagle): Ruff! 
Wishbone (Terrier): Ruff! 
Wishbone (Terrier): Ruff! 
Wishbone (Terrier): Ruff! 

dog.hs:

#!/usr/bin/env runhaskell 

import Control.Monad (replicateM_) 

data Dog = Dog { 
     name :: String, 
     breed :: String, 
     barks :: Int 
    } 

defaultDog :: Dog 
defaultDog = Dog { 
     name = "Dog", 
     breed = "Beagle", 
     barks = 2 
    } 

bark :: Dog -> IO() 
bark dog = replicateM_ (barks dog) $ putStrLn $ (name dog) ++ " (" ++ (breed dog) ++ "): Ruff!" 

main :: IO() 
main = do 
    bark $ defaultDog { 
      name = "Snoopy", 
      barks = 2 
     } 

    bark $ defaultDog { 
      name = "Wishbone", 
      breed = "Terrier", 
      barks = 3 
     } 
+0

यह वही बात है जो Ionuţ ने सुझाव दिया था। उनके उत्तर पर मेरी टिप्पणी देखें। –

संबंधित मुद्दे