2009-12-28 20 views
5

मेरा पहला हास्केल प्रोग्राम यहां है। आप बेहतर तरीके से कौन से हिस्से लिखेंगे?मेरे पहले हैकेल कार्यक्रम में क्या सुधार किया जा सकता है?

-- Multiplication table 
-- Returns n*n multiplication table in base b 

import Text.Printf 
import Data.List 
import Data.Char 

-- Returns n*n multiplication table in base b 
mulTable :: Int -> Int -> String 
mulTable n b = foldl (++) (verticalHeader n b w) (map (line n b w) [0..n]) 
       where 
       lo = 2* (logBase (fromIntegral b) (fromIntegral n)) 
       w = 1+fromInteger (floor lo) 

verticalHeader :: Int -> Int -> Int -> String 
verticalHeader n b w = (foldl (++) tableHeader columnHeaders) 
         ++ "\n" 
         ++ minusSignLine 
         ++ "\n" 
        where 
        tableHeader = replicate (w+2) ' ' 
        columnHeaders = map (horizontalHeader b w) [0..n] 
        minusSignLine = concat (replicate ((w+1)* (n+2)) "-") 

horizontalHeader :: Int -> Int -> Int -> String 
horizontalHeader b w i = format i b w 

line :: Int -> Int -> Int -> Int -> String 
line n b w y = (foldl (++) ((format y b w) ++ "|") 
          (map (element b w y) [0..n])) ++ "\n" 

element :: Int -> Int -> Int -> Int -> String 
element b w y x = format (y * x) b w 

toBase :: Int -> Int -> [Int] 
toBase b v = toBase' [] v where 
    toBase' a 0 = a 
    toBase' a v = toBase' (r:a) q where (q,r) = v `divMod` b 

toAlphaDigits :: [Int] -> String 
toAlphaDigits = map convert where 
    convert n | n < 10 = chr (n + ord '0') 
      | otherwise = chr (n + ord 'a' - 10) 

format :: Int -> Int -> Int -> String 
format v b w = concat spaces ++ digits ++ " " 
       where 
        digits = if v == 0 
          then "0" 
          else toAlphaDigits (toBase b v) 
        l = length digits 
        spaceCount = if (l > w) then 0 else (w-l) 
        spaces = replicate spaceCount " " 
+1

आप परीक्षण है? वे अपने आप में कुछ सुधार प्रकट कर सकते हैं। –

उत्तर

11

यहाँ कुछ सुझाव हैं:

  • गणना के tabularity और अधिक स्पष्ट करने के लिए, मैं नहीं बल्कि n गुजर से line कार्य करने के लिए सूची [0..n] से होकर गुजरेगा।

  • मैं आगे क्षैतिज और ऊर्ध्वाधर अक्ष की गणना को विभाजित कर दूंगा ताकि वे गणना के बजाए mulTable पर तर्क के रूप में पारित हो जाएं।

  • हास्केल उच्च-आदेश है, और लगभग गणना में से कोई भी गुणा के साथ नहीं करना है। तो मैं mulTable से binopTable का नाम बदलूंगा और वास्तविक गुणा को पैरामीटर के रूप में पास कर दूंगा।

  • अंत में, व्यक्तिगत संख्याओं का स्वरूपण दोहराया जाता है। पैरामीटर के रूप में \x -> format x b w पास क्यों न करें, b और w की आवश्यकता को समाप्त करें?

मैं जो सुझाव दे रहा हूं उसका शुद्ध प्रभाव यह है कि आप बाइनरी ऑपरेटरों के लिए टेबल बनाने के लिए एक सामान्य उच्च-आदेश फ़ंक्शन बनाते हैं। अपने प्रकार

binopTable :: (i -> String) -> (i -> i -> i) -> [i] -> [i] -> String 

की तरह कुछ हो जाता है और आप उदाहरण के लिए एक बहुत अधिक पुन: प्रयोज्य समारोह — साथ हवा, बूलियन सच्चाई टेबल केक का एक टुकड़ा होना चाहिए।

उच्च-आदेश और पुन: प्रयोज्य हास्केल वे है।

+0

आपकी टिप्पणियों के लिए धन्यवाद। – danatel

4

0) को जोड़ने के लिए एक मुख्य कार्य :-) कम से कम अल्पविकसित

import System.Environment (getArgs) 
import Control.Monad (liftM) 

main :: IO() 
main = do 
    args <- liftM (map read) $ getArgs 
    case args of 
    (n:b:_) -> putStrLn $ mulTable n b 
    _  -> putStrLn "usage: nntable n base" 

1) चलाने ghc या runhaskell-Wall के साथ; hlint के माध्यम से चलाएं।

hlint कुछ विशेष सुझाव नहीं देता है, तब तक यहां (केवल कुछ अनावश्यक कोष्ठक), ghc आपको बता देंगे कि आप वास्तव में Text.Printf यहाँ की जरूरत नहीं है ...

2) आधार से चलाने की कोशिश = 1 या आधार = 0 या आधार = -1

+1

निश्चित रूप से मैं अकेला नहीं हूं जो ज्यादातर ghci के माध्यम से कोड चलाता है, और केवल बाद में एक 'मुख्य' जोड़ता है ... –

+0

'ghci' बहुत अच्छा है, लेकिन मैं वहां केवल छोटे स्निपेट चलाता हूं ... (हाँ,' : लोड' भी सहायक है)। – sastanin

+1

मैं परीक्षण करने के लिए जीएचसीआई के माध्यम से अपने कोड के छोटे टुकड़े चलाता हूं, लेकिन मेरे पास हमेशा अंत चरणों के दौरान मुख्य घोषणाएं होती हैं, इसलिए मैं पूरे कार्यक्रम को रनघैक के साथ चला सकता हूं और इसका परीक्षण कर सकता हूं। – Rayne

11

आप import Text.Printf से कुछ भी उपयोग नहीं करते हैं।

स्टाइलिस्टिक रूप से, आप आवश्यकतानुसार अधिक कोष्ठक का उपयोग करते हैं। हास्केलर्स को कोड को और अधिक पठनीय लगता है जब इसे इस तरह की अपर्याप्त सामग्री से साफ़ किया जाता है। h x = f (g x) जैसे कुछ के बजाय, h = f . g लिखें।

यहां कुछ भी वास्तव में Int की आवश्यकता नहीं है; (Integral a) => a करना चाहिए।

foldl (++) x xs == concat $ x : xs: मुझे आपके कार्यान्वयन से बेहतर काम करने के लिए अंतर्निहित concat पर भरोसा है।
इसके अलावा, आपको foldr पसंद करना चाहिए जब फ़ंक्शन अपने दूसरे तर्क में आलसी है, (++) – है क्योंकि हास्केल आलसी है, इससे स्टैक स्पेस कम हो जाता है (और अनंत सूचियों पर भी काम करता है)।
इसके अलावा, unwords और unlinesintercalate " " और concat . map (++ "\n") क्रमशः concat . map (++ "\n") के लिए शॉर्टकट हैं, यानी "रिक्त स्थान के साथ जुड़ें" और "न्यूलाइन के साथ जुड़ें (साथ ही साथ नई लाइन के साथ जुड़ें); आप उन चीज़ों को दो चीजों से बदल सकते हैं।

जब तक आप बड़ी संख्या का उपयोग नहीं करते, w = length $ takeWhile (<= n) $ iterate (* b) 1 शायद तेज़ है। या, आलसी प्रोग्रामर के मामले में, w = length $ toBase b n दें।

concat ((replicate ((w+1)* (n+2)) "-") == replicate ((w+1) * (n+2)) '-' – यह सुनिश्चित नहीं है कि आप इसे कैसे चूक गए हैं, आपको बस कुछ ही पंक्तियां मिल गईं।

आप भी concat spaces के साथ एक ही काम करते हैं। हालांकि, वास्तव में Text.Printf आयात और printf "%*s " w digits लिखना आसान नहीं होगा?

+0

आपकी टिप्पणियों के लिए धन्यवाद। – danatel

2

आप बहु टिप्पणियां उपयोग करना चाहते हैं:

{- A multiline 
    comment -} 

इसके अलावा, foldl का उपयोग कभी नहीं, बजाय foldl' उपयोग करते हैं, उन मामलों में जहां आप बड़े सूचियों जो तह किया जाना चाहिए के साथ काम कर रहे हैं। यह अधिक स्मृति कुशल है।

+5

'foldl'' अगर फ़ंक्शन सख्त है, तो यह' आलसी 'है। –

1

एक संक्षिप्त टिप्पणी यह ​​बताती है कि प्रत्येक कार्य क्या करता है, इसके तर्क और वापसी मूल्य हमेशा अच्छा होता है। मुझे पूरी तरह से समझने के लिए कोड को सावधानी से पढ़ना पड़ा।

कुछ लोग कहेंगे कि यदि आप ऐसा करते हैं, तो स्पष्ट प्रकार के हस्ताक्षर की आवश्यकता नहीं हो सकती है। यह एक सौंदर्य सवाल है, मेरे पास इसकी कोई मजबूत राय नहीं है।

एक नाबालिग चेतावनी: "। Monomorphism प्रतिबंध" अगर आप प्रकार हस्ताक्षर को दूर करते हैं, आप स्वचालित रूप से बहुरूपी Integral समर्थन ephemient उल्लेख मिल जाएगा, लेकिन आप अभी भी कुख्यात की वजह से toAlphaDigits के चारों ओर एक की आवश्यकता होगी

+0

मेरा मानना ​​है कि, अच्छे फ़ंक्शन नामकरण और स्पष्ट प्रकार के हस्ताक्षर के बाद, टिप्पणियां आवश्यक नहीं हो सकती हैं। और मैं खुद को अंग्रेजी में तुलना में हास्केल छद्म कोड (जो वास्तव में हास्केल कोड की तरह दिखता है) में व्यक्त करता हूं।लेकिन, जैसा कि आप ध्यान देते हैं, यह लेखक के सौंदर्य स्वाद पर निर्भर है। – ephemient

+0

यह सच है, मुझे लगता है कि यह सिर्फ नामों, विशेष रूप से पैरामीटर नामों पर अधिक जोर देता है। उदाहरण के लिए, 'वर्टिकल हैडर एन बी डब्ल्यू' वास्तव में बाहर नहीं निकलता है और चिल्लाता है कि उस कार्य को क्या करने की उम्मीद करनी चाहिए। – Dan

5

नॉर्मन रैमसे ने उत्कृष्ट उच्च स्तरीय (डिज़ाइन) सुझाव दिए; नीचे कुछ निम्न-स्तर वाले हैं:

  • सबसे पहले, HLint से परामर्श लें। एचलिंट एक दोस्ताना कार्यक्रम है जो आपको अपने हास्केल कोड को सुधारने के तरीके पर प्राथमिक सलाह देता है!
    • आपके मामले में एचलिंट 7 सुझाव देता है। (ज्यादातर अनावश्यक ब्रैकेट्स के बारे में)
    • एचएलआईंट के सुझावों के अनुसार अपना कोड संशोधित करें जब तक कि आप इसे पसंद न करें।
  • अधिक HLint की तरह सामान:
    • concat (replicate i "-")replicate i '-' क्यों नहीं?
  • Hoogle के साथ परामर्श करें जब भी यह मानने का कारण हो कि आपके लिए आवश्यक फ़ंक्शन पहले से ही हास्केल के पुस्तकालयों में उपलब्ध है। हास्केल कई उपयोगी कार्यों के साथ आता है ताकि होगल को अक्सर काम में आना चाहिए।
    • तारों को संयोजित करने की आवश्यकता है? [String] -> String के लिए खोजें, और वॉयला आपको concat मिला। अब उन सभी फ़ोल्डरों को प्रतिस्थापित करें।
    • पिछली खोज ने भी unlines का सुझाव दिया। असल में, यह आपकी आवश्यकताओं के अनुरूप भी बेहतर है। यह जादू है!
  • वैकल्पिक: होल एम और होलिंट बनाने के लिए अपने दिल में नील एम को रोकें और धन्यवाद, और अन्य अच्छे सामान जैसे हास्केल, पुल, टेनिस गेंद और स्वच्छता बनाने के लिए धन्यवाद।
  • अब, प्रत्येक फ़ंक्शन के लिए जो एक ही प्रकार के कई तर्क लेता है, इसे स्पष्ट करें कि इसका अर्थ क्या है, उन्हें वर्णनात्मक नाम देकर। यह टिप्पणियों से बेहतर है, लेकिन आप अभी भी दोनों का उपयोग कर सकते हैं।

तो

-- Returns n*n multiplication table in base b 
mulTable :: Int -> Int -> String 
mulTable n b = 

mulTable :: Int -> Int -> String 
mulTable size base = 
  • हो जाता है पिछले सुझाव के अतिरिक्त वर्ण झटका नरम करने के लिए: जब एक समारोह केवल एक बार प्रयोग किया जाता है, और अपने आप में बहुत ही उपयोगी नहीं है , इसे अपने where खंड में अपने कॉलर के दायरे में डाल दें, जहां यह कॉलर्स के चर का उपयोग कर सकता है, जिससे आपको सबकुछ पास करने की ज़रूरत होती है।

तो

line :: Int -> Int -> Int -> Int -> String 
line n b w y = 
    concat 
    $ format y b w 
    : "|" 
    : map (element b w y) [0 .. n] 

element :: Int -> Int -> Int -> Int -> String 
element b w y x = format (y * x) b w 

line :: Int -> Int -> Int -> Int -> String 
line n b w y = 
    concat 
    $ format y b w 
    : "|" 
    : map element [0 .. n] 
    where 
    element x = format (y * x) b w 
  • हो जाता है तुम भी mulTable के where खंड में line स्थानांतरित कर सकते हैं; imho, आपको चाहिए।
    • यदि आपको where क्लॉज को where क्लॉज परेशान करने के अंदर घोंसला मिलता है, तो मैं आपके इंडेंटेशन आदतों को बदलने का सुझाव देता हूं। मेरी सिफारिश हमेशा 2 या हमेशा 4 रिक्त स्थान के लगातार इंडेंटेशन का उपयोग करना है। फिर आप आसानी से देख सकते हैं, हर जगह, जहां where अन्य where में है। ठीक

नीचे क्या यह (शैली में कुछ अन्य बदलाव के साथ) की तरह लग रहा है:

import Data.List 
import Data.Char 

mulTable :: Int -> Int -> String 
mulTable size base = 
    unlines $ 
    [ vertHeaders 
    , minusSignsLine 
    ] ++ map line [0 .. size] 
    where 
    vertHeaders = 
     concat 
     $ replicate (cellWidth + 2) ' ' 
     : map horizontalHeader [0 .. size] 
    horizontalHeader i = format i base cellWidth 
    minusSignsLine = replicate ((cellWidth + 1) * (size + 2)) '-' 
    cellWidth = length $ toBase base (size * size) 
    line y = 
     concat 
     $ format y base cellWidth 
     : "|" 
     : map element [0 .. size] 
     where 
     element x = format (y * x) base cellWidth 

toBase :: Integral i => i -> i -> [i] 
toBase base 
    = reverse 
    . map (`mod` base) 
    . takeWhile (> 0) 
    . iterate (`div` base) 

toAlphaDigit :: Int -> Char 
toAlphaDigit n 
    | n < 10 = chr (n + ord '0') 
    | otherwise = chr (n + ord 'a' - 10) 

format :: Int -> Int -> Int -> String 
format v b w = 
    spaces ++ digits ++ " " 
    where 
    digits 
     | v == 0 = "0" 
     | otherwise = map toAlphaDigit (toBase b v) 
    spaces = replicate (w - length digits) ' ' 
+1

बहुत बहुत धन्यवाद। इस सवाल के साथ मैं वास्तव में नाखुश हूं कि मैं एक से अधिक जवाब स्वीकार नहीं कर सकता! मैंने आपकी प्रतिक्रिया से बहुत कुछ सीखा है। – danatel

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