হাস্কেল - putStrLn- তে খাঁটি ফাংশন এবং পার্শ্ব প্রতিক্রিয়া বোঝা


10

সম্প্রতি, আমি হাস্কেল শিখতে শুরু করলাম কারণ আমি ফাংশনাল প্রোগ্রামিংয়ের উপর আমার জ্ঞানকে আরও প্রসারিত করতে চেয়েছিলাম এবং আমাকে অবশ্যই বলতে হবে যে আমি এখনও পর্যন্ত এটি সত্যই ভালবাসি। আমি বর্তমানে যে সংস্থানটি ব্যবহার করছি তা হ'ল বহুবচনের উপর 'হাস্কেল ফান্ডামেন্টাল পার্ট 1' কোর্স। দুর্ভাগ্যক্রমে আমি নীচের কোড সম্পর্কে প্রভাষকের একটি বিশেষ উদ্ধৃতি বুঝতে কিছু অসুবিধা পেয়েছি এবং আশা করছি যে আপনি ছেলেরা এই বিষয়ে কিছুটা আলোকপাত করতে পারে।

সংযুক্ত কোড

helloWorld :: IO ()
helloWorld = putStrLn "Hello World"

main :: IO ()
main = do
    helloWorld
    helloWorld
    helloWorld

উদ্ধৃতি

ডু-ব্লকে আপনার যদি একই আইও অ্যাকশন একাধিকবার হয়, তবে এটি একাধিকবার চালানো হবে। সুতরাং এই প্রোগ্রামটি তিনবার 'হ্যালো ওয়ার্ল্ড' স্ট্রিংটি প্রিন্ট করে। এই উদাহরণটি চিত্রিত করতে সহায়তা putStrLnকরে যা পার্শ্ব প্রতিক্রিয়াযুক্ত কোনও ফাংশন নয়। ভেরিয়েবল putStrLnসংজ্ঞায়িত করার জন্য আমরা ফাংশনটি একবার কল করি helloWorld। যদি putStrLnস্ট্রিং মুদ্রণ একটি পার্শ্ব প্রভাব ফেলেছে, এটি শুধুমাত্র একবার প্রিন্ট হবে এবং helloWorldপরিবর্তনশীল প্রধান করণীয়-ব্লক পুনরাবৃত্তি কোনো প্রভাব নেই।

অন্যান্য বেশিরভাগ প্রোগ্রামিং ভাষায়, এই জাতীয় প্রোগ্রামটি কেবল একবার 'হ্যালো ওয়ার্ল্ড' প্রিন্ট করবে, যেহেতু putStrLnফাংশনটি যখন ডাকা হত তখন মুদ্রণটি ঘটত । এই সূক্ষ্ম পার্থক্যটি প্রায়শই প্রাথমিকভাবে যাত্রীদের যাত্রা শুরু করে, তাই এটি সম্পর্কে কিছুটা চিন্তা করুন এবং নিশ্চিত করুন যে আপনি এই প্রোগ্রামটি কেন 'হ্যালো ওয়ার্ল্ড' তিনবার মুদ্রণ করেন এবং যদি putStrLnফাংশনটি পার্শ্ব প্রতিক্রিয়া হিসাবে মুদ্রণটি করে তবে এটি কেন একবার মুদ্রণ করবে ।

আমি কি বুঝতে পারি না

আমার কাছে এটি প্রায় স্বাভাবিক মনে হয় যে 'হ্যালো ওয়ার্ল্ড' স্ট্রিংটি তিনবার মুদ্রিত হয়েছে। আমি helloWorldপরিবর্তনশীল (বা ফাংশন?) কে কলব্যাকের এক ধরণের হিসাবে বুঝতে পারি যা পরে অনুরোধ করা হয়। আমি যা বুঝতে পারি না তা হল, কীভাবে যদি putStrLnকোনও পার্শ্ব প্রতিক্রিয়া থাকে তবে তার ফলে স্ট্রিংটি কেবল একবার মুদ্রিত হবে। বা কেন এটি কেবলমাত্র অন্যান্য প্রোগ্রামিং ভাষায় একবার মুদ্রিত হবে।

আসুন সি # কোডে বলি, আমি অনুমান করি এটি দেখতে এটির মতো দেখাচ্ছে:

সি # (ফিডাল)

using System;

public class Program
{
    public static void HelloWorld()
    {
        Console.WriteLine("Hello World");
    }

    public static void Main()
    {
        HelloWorld();
        HelloWorld();
        HelloWorld();
    }
}

আমি নিশ্চিত যে আমি বেশ সাধারণ কিছু উপেক্ষা করছি বা তার পরিভাষাটির ভুল ব্যাখ্যা দিয়েছি। কোন সাহায্যের ব্যাপকভাবে প্রশংসা হবে।

সম্পাদনা করুন:

আপনার উত্তরের জন্য সবাইকে ধন্যবাদ! আপনার উত্তরগুলি আমাকে এই ধারণাগুলির আরও ভাল ধারণা পেতে সহায়তা করেছে। আমি এখনও এটি পুরোপুরি ক্লিক হয়েছে বলে মনে করি না, তবে ভবিষ্যতে আমি বিষয়টি আবার ঘুরে দেখব, ধন্যবাদ!


2
helloWorldC # তে ক্ষেত্র বা চলক হিসাবে ধ্রুবক হওয়ার কথা ভাবুন । এমন কোনও পরামিতি প্রয়োগ করা হচ্ছে না helloWorld
ক্যারামিরিল

2
putStrLn এর কোনও পার্শ্ব প্রতিক্রিয়া নেই; এটি কেবল কোনও আইও ক্রিয়া প্রদান করে, আপনি যতবার কল করুন না কেন যুক্তির জন্য একই আইও ক্রিয়া । "Hello World"putStrLn
চিপনার

1
যদি এটি ঘটে থাকে helloworldতবে এমন কোনও ক্রিয়া মুদ্রিত হবে না Hello world; এটি মুদ্রণের putStrLn পরে ফেরত মূল্য হবেHello World (যথা, ())।
চিপনার

2
আমি এই উদাহরণটি বুঝতে চাইছি আপনাকে ইতিমধ্যে বুঝতে হবে হাসকেলের পার্শ্ব প্রতিক্রিয়া কীভাবে কাজ করে। এটি একটি ভাল উদাহরণ নয়।
ব্যবহারকারী 253751

আপনার সি # স্নিপেটে আপনি পছন্দ করেন না helloWorld = Console.WriteLine("Hello World");। আপনি শুধু ধারণ Console.WriteLine("Hello World");মধ্যে HelloWorldফাংশন নিষ্পন্ন করা হবে সব HelloWorldপ্রার্থনা করা হয়। এখন কি করে helloWorld = putStrLn "Hello World"তোলে তা চিন্তা করুন helloWorld। এটি একটি আইও মনাদকে দেওয়া হয় যা এতে থাকে ()। একবার আপনি এটি দ্বারা বেঁধে রাখার পরে >>=কেবল এটির ক্রিয়াকলাপটি সম্পাদন করা হবে ( ()কোনও কিছু মুদ্রণ করা) এবং আপনাকে বাইন্ড অপারেটরের ডানদিকে দেবে।
রেডু

উত্তর:


8

আমরা যদি helloWorldস্থানীয় ভেরিয়েবল হিসাবে সংজ্ঞায়িত করি তবে লেখকটির অর্থ কী তা বোঝার জন্য এটি সম্ভবত সহজ হবে :

main :: IO ()
main = do
  let helloWorld = putStrLn "Hello World!"
  helloWorld
  helloWorld
  helloWorld

যা আপনি এই সি # -র মতো সিউডোকোডের সাথে তুলনা করতে পারেন:

void Main() {
  var helloWorld = {
    WriteLine("Hello World!")
  }
  helloWorld;
  helloWorld;
  helloWorld;
}

আই সি ইন # WriteLineএটি এমন একটি প্রক্রিয়া যা এর যুক্তি মুদ্রণ করে এবং কিছুই দেয় না। হাস্কেল-এ, putStrLnএকটি ফাংশন যা একটি স্ট্রিং নেয় এবং আপনাকে এমন একটি ক্রিয়া দেয় যা সেই স্ট্রিংটি কার্যকর করতে পারলে মুদ্রণ করবে would এর অর্থ লেখার মধ্যে একেবারেই কোনও পার্থক্য নেই

do
  let hello = putStrLn "Hello World"
  hello
  hello

এবং

do
  putStrLn "Hello World"
  putStrLn "Hello World"

বলা হচ্ছে, এই উদাহরণে পার্থক্যটি বিশেষভাবে গভীর নয়, সুতরাং লেখক এই বিভাগে যা পেতে চাইছেন এবং যদি আপনি এখনই সরিয়ে যান, আপনি যদি বেশ কিছু না পান তবে এটি ঠিক আছে।

অজগরটির সাথে তুলনা করলে এটি কিছুটা ভাল কাজ করে

hello_world = print('hello world')
hello_world
hello_world
hello_world

এখানে বিন্দু হচ্ছে যে মধ্যে Haskell আই ক্রিয়া "বাস্তব" মান যে আরও "callbacks" বা সাজানোর কিছু আবৃত করা প্রয়োজন হবে না তাদের নির্বাহ থেকে বিরত রয়েছে - বরং একমাত্র উপায় কি হল চালানো তাদের পেতে এগুলিকে একটি নির্দিষ্ট জায়গায় রাখার জন্য (যেমন কোথাও কোথাও কোথাও mainবা কোনও সুতোর প্রসার ঘটে main)।

এটি কেবল একটি পার্লার ট্রিক নয়, এটি আপনার কোড কীভাবে লিখবে তার উপর কিছু আকর্ষণীয় প্রভাব ফেলবে (উদাহরণস্বরূপ, এটি হেস্কেলকে যে সাধারণ নিয়ন্ত্রণ কাঠামোগুলির সাথে আপনি পরিচিত হতে চাইছেন না তার কারণ এটিই একটি কারণ part অপরিহার্য ভাষাগুলি থেকে এবং পরিবর্তে ফাংশনের দিক দিয়ে সমস্ত কিছু করে পালিয়ে যেতে পারি) তবে আমি আবার এ নিয়ে খুব বেশি চিন্তা করব না (এই জাতীয় উপমাগুলি সর্বদা অবিলম্বে ক্লিক করে না)


4

আপনি যদি এমন কোনও ফাংশন ব্যবহার করেন যা তার পরিবর্তে আসলে কিছু করে তবে এটি বর্ণিত পার্থক্যটি দেখতে আরও সহজ হতে পারে helloWorld। নিম্নলিখিত সম্পর্কে চিন্তা করুন:

add :: Int -> Int -> IO Int
add x y = do
  putStrLn ("I am adding " ++ show x ++ " and " ++ show y)
  return (x + y)

plus23 :: IO Int
plus23 = add 2 3

main :: IO ()
main = do
  _ <- plus23
  _ <- plus23
  _ <- plus23
  return ()

এটি "আমি 2 এবং 3 যোগ করছি" 3 বার প্রিন্ট আউট করব।

সি # তে, আপনি নিম্নলিখিত লিখতে পারেন:

using System;

public class Program
{
    public static int add(int x, int y)
    {
        Console.WriteLine("I am adding {0} and {1}", x, y);
        return x + y;
    }

    public static void Main()
    {
        int x;
        int plus23 = add(2, 3);
        x = plus23;
        x = plus23;
        x = plus23;
        return;
    }
}

যা শুধুমাত্র একবার মুদ্রণ হবে।


3

putStrLn "Hello World"পার্শ্ব প্রতিক্রিয়াগুলির যদি মূল্যায়ণ হয় , তবে বার্তাটি কেবল একবার মুদ্রিত হবে।

আমরা নিম্নলিখিত কোড সহ সেই দৃশ্যটি আনুমানিক করতে পারি:

import System.IO.Unsafe (unsafePerformIO)
import Control.Exception (evaluate)

helloWorld :: ()
helloWorld = unsafePerformIO $ putStrLn "Hello World"

main :: IO ()
main = do
    evaluate helloWorld
    evaluate helloWorld
    evaluate helloWorld

unsafePerformIOকোনও IOপদক্ষেপ নেয় এবং এটি একটি ক্রিয়া "ভুলে যায়" IO, IOক্রিয়া রচনার দ্বারা আরোপিত যথাক্রমে ক্রমবর্ধমান থেকে অযত্নে এবং অলস মূল্যায়নের ভায়োরিয়া অনুযায়ী প্রভাবটি ঘটতে দেয় (বা না)।

evaluateএকটি খাঁটি মান গ্রহণ করে এবং নিশ্চিত করে যে যখনই ফলাফলের IOক্রিয়াটি মূল্যায়ন করা হয় the যা আমাদের জন্য এটি হবে কারণ এটি মূল্য নির্ধারণ করে main। আমরা প্রোগ্রামটির শুরুর সাথে কিছু মানের মূল্যায়ন সংযোগ করতে এটি এখানে ব্যবহার করছি।

এই কোডটি কেবল একবার "হ্যালো ওয়ার্ল্ড" মুদ্রণ করে। আমরা helloWorldখাঁটি মূল্য হিসাবে বিবেচনা করি । তবে এর অর্থ এটি সমস্ত evaluate helloWorldকলগুলির মধ্যে ভাগ করা হবে । এবং কেন না? এটি সর্বোপরি একটি খাঁটি মূল্য, কেন এটি অযথা পুনরায় গণনা করতে হবে? প্রথম evaluateক্রিয়াটি "লুকানো" প্রভাবটিকে "পপ" করে এবং পরবর্তী ক্রিয়াগুলি ফলাফলটির মূল্যায়ন করে (), যা পরবর্তী কোনও প্রভাবের কারণ হয় না।


1
এটি লক্ষণীয় যে আপনি unsafePerformIOহাস্কেল শেখার এই পর্যায়ে একেবারে ব্যবহার করা উচিত নয় । একটি কারণে এটির নামটিতে "অনিরাপদ" রয়েছে এবং আপনি যদি ব্যবহার না করা (এবং না) প্রসঙ্গে সাবধানতার সাথে বিবেচনা না করতে পারেন তবে আপনার এটি ব্যবহার করা উচিত নয়। দানিয়াডিয়াজ উত্তরে যে কোডটি রেখেছিল তা পুরোপুরি অনন্য আচরণের ধরণটি ধারণ করে যা ফলাফল হতে পারে unsafePerformIO
অ্যান্ড্রু রে

1

লক্ষ্য করার জন্য একটি বিশদ রয়েছে: putStrLnসংজ্ঞা দেওয়ার সময় আপনি কেবল একবার ফাংশন কল করেন helloWorld। ইন mainফাংশন আপনি ঠিক যে ফেরত মান ব্যবহার putStrLn "Hello, World"তিনবার।

প্রভাষক বলেছেন যে putStrLnকলটির কোনও পার্শ্ব প্রতিক্রিয়া নেই এবং এটি সত্য। তবে এর ধরণটি দেখুন helloWorld- এটি একটি আইও ক্রিয়া। putStrLnএটি কেবল আপনার জন্য তৈরি করে। পরে, আপনি তাদের মধ্যে 3 টি doব্লকের সাথে আরও একটি আইও ক্রিয়া তৈরি করতে চেইন করেন - main। পরে, আপনি যখন আপনার প্রোগ্রামটি সম্পাদন করবেন, তখন সেই ক্রিয়াটি চালানো হবে, সেখানেই পার্শ্ব প্রতিক্রিয়া রয়েছে।

এর ভিত্তিতে ব্যবস্থাপত্রটি - মনডস । এই শক্তিশালী ধারণাটি আপনাকে এমন কোনও ভাষায় মুদ্রণের মতো কিছু পার্শ্ব প্রতিক্রিয়া ব্যবহার করতে দেয় যা পার্শ্ব প্রতিক্রিয়াগুলি সরাসরি সমর্থন করে না। আপনি কেবল কিছু ক্রিয়া শৃঙ্খলাবদ্ধ করেন এবং সেই শৃঙ্খলা আপনার প্রোগ্রামের শুরুতে চালানো হবে। আপনি যদি হাস্কেলকে গুরুত্বের সাথে ব্যবহার করতে চান তবে আপনাকে সেই ধারণাটি গভীরভাবে বুঝতে হবে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.