ল্যাম্বদা ফাংশন এবং তাদের পরামিতিগুলির সুযোগ?


92

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

সুতরাং আমার নীচে নিম্নলিখিত সরলীকৃত কোড রয়েছে:

def callback(msg):
    print msg

#creating a list of function handles with an iterator
funcList=[]
for m in ('do', 're', 'mi'):
    funcList.append(lambda: callback(m))
for f in funcList:
    f()

#create one at a time
funcList=[]
funcList.append(lambda: callback('do'))
funcList.append(lambda: callback('re'))
funcList.append(lambda: callback('mi'))
for f in funcList:
    f()

এই কোডের আউটপুট হল:

mi
mi
mi
do
re
mi

আমি আশা করেছিলাম:

do
re
mi
do
re
mi

কেন একটি পুনরায় ব্যবহারকারীর ব্যবহারের সাথে গোলযোগ হয়েছে?

আমি একটি ডিপকপি ব্যবহার করার চেষ্টা করেছি:

import copy
funcList=[]
for m in ('do', 're', 'mi'):
    funcList.append(lambda: callback(copy.deepcopy(m)))
for f in funcList:
    f()

তবে এটি একই সমস্যা আছে।


4
আপনার প্রশ্নের শিরোনাম কিছুটা বিভ্রান্তিকর।
lispmachine

4
ল্যাম্বডাস যদি আপনি বিভ্রান্ত দেখেন তবে কেন ব্যবহার করবেন? ফাংশন সংজ্ঞায়িত করতে ডিফ ব্যবহার করবেন না কেন? এটি আপনার সমস্যা সম্পর্কে কী যা ল্যাম্বডাসকে এত গুরুত্বপূর্ণ করে তোলে?
এসলট

@ এস.লোট নেস্টেড ফাংশনটির ফলে একই সমস্যা হবে (সম্ভবত আরও স্পষ্টভাবে দৃশ্যমান)
লিসপম্যাচাইন

4
@ গার্টল্যান্ড: তুমি কি আমি? আমিও জিইউআই ইভেন্টগুলিতে কাজ করছিলাম এবং ব্যাকগ্রাউন্ড গবেষণার সময় এই পৃষ্ঠাটি সন্ধান করার আগে আমি নিম্নলিখিত প্রায় অভিন্ন পরীক্ষা লিখেছিলাম: পেস্টবিন.
com

4
দেখুন লাম্বডাস কেন বিভিন্ন লুপের সাথে একটি লুপে সংজ্ঞায়িত হয় যা সমস্ত একই ফলাফল দেয়? পাইথনের অফিসিয়াল প্রোগ্রামিং এফএকিউতে। এটি সমস্যাটি বেশ সুন্দরভাবে ব্যাখ্যা করে এবং একটি সমাধান দেয়।
23.314

উত্তর:


80

এখানে সমস্যাটি হল mভেরিয়েবল (একটি রেফারেন্স) আশেপাশের সুযোগ থেকে নেওয়া। ল্যাম্বডা স্কোপটিতে কেবল পরামিতিগুলি রাখা হয়।

এটি সমাধান করার জন্য আপনাকে ল্যাম্বদার জন্য আরও একটি সুযোগ তৈরি করতে হবে:

def callback(msg):
    print msg

def callback_factory(m):
    return lambda: callback(m)

funcList=[]
for m in ('do', 're', 'mi'):
    funcList.append(callback_factory(m))
for f in funcList:
    f()

উপরের উদাহরণে ল্যাম্বদা সন্ধানের জন্য বিস্ময়কর সুযোগও ব্যবহার করে m, তবে এবার এটি callback_factoryস্কোপ যা প্রতি callback_factory কল প্রতি একবার তৈরি হয় ।

বা ফান্টুলগুলি সহ

from functools import partial

def callback(msg):
    print msg

funcList=[partial(callback, m) for m in ('do', 're', 'mi')]
for f in funcList:
    f()

4
এই ব্যাখ্যাটি কিছুটা বিভ্রান্তিকর। সমস্যাটি হল পুনরাবৃত্তিতে এম এর মান পরিবর্তন, সুযোগটি নয়।
আইএক্সএক্স

উপরের মন্তব্যটি সত্য যেমন @ বাবার্ন্ট দ্বারা মন্তব্য করা হয়েছে যেখানে এই প্রশ্নে একটি লিঙ্ক দেওয়া হয়েছে যা ফেনোনিমন এবং সমাধান ব্যাখ্যা করে। ফ্যাক্টরি পদ্ধতিটি একই প্রভাব সরবরাহ করে যেমন কারখানা পদ্ধতিতে যুক্তিটি ল্যাম্বডায় স্কোপ লোকাল সহ একটি নতুন ভেরিয়েবল তৈরি করার প্রভাব ফেলে। তবে প্রদত্ত সমাধানটি ল্যাম্বডায় কোনও যুক্তি না থাকায় সিনট্যাকটিক্যালি কাজ করে না - এবং নীচের লামদা দ্রবণে ল্যাম্বদাও ল্যাম্বদা তৈরির জন্য একটি নতুন অবিচল পদ্ধতি তৈরি না করে একই প্রভাব সরবরাহ করে
মার্ক প্যারিস

134

যখন একটি ল্যাম্বদা তৈরি করা হয়, তখন এটি ব্যবহার করা হয় এমন ঘেরে ভেরিয়েবলের অনুলিপি তৈরি করে না। এটি পরিবেশের একটি রেফারেন্স বজায় রাখে যাতে এটি পরে ভেরিয়েবলের মান সন্ধান করতে পারে। একটি মাত্র আছে m। এটি প্রতিবার লুপের মাধ্যমে বরাদ্দ করা হয়। লুপের পরে, ভেরিয়েবলের mমান থাকে 'mi'। সুতরাং আপনি যখন পরে তৈরির ফাংশনটি বাস্তবে চালাবেন, এটি এটি তৈরির mপরিবেশে এর মান সন্ধান করবে, ততক্ষণে এর মান হবে 'mi'

এই সমস্যার একটি সাধারণ এবং মুশকিল সমাধান হ'ল mল্যাম্বডাকে timeচ্ছিক প্যারামিটারের ডিফল্ট আর্গুমেন্ট হিসাবে ব্যবহার করে তৈরি করা সময়ের মান ক্যাপচার করা। আপনি সাধারণত একই নামের একটি প্যারামিটার ব্যবহার করেন যাতে আপনার কোডের বডি পরিবর্তন করতে হবে না:

for m in ('do', 're', 'mi'):
    funcList.append(lambda m=m: callback(m))

6
চমৎকার সমাধান! তাত্পর্যপূর্ণ হলেও, আমি অনুভব করি যে আসল অর্থটি অন্যান্য বাক্য গঠনগুলির চেয়ে পরিষ্কার।
কোয়ান্টাম 7

4
এটি সম্পর্কে হ্যাকিশ বা কৌতুকপূর্ণ কিছুই নেই; এটি পাইথন এফএকিউ এর ঠিক একই সমাধান। এখানে দেখুন ।
23:34

4
@ বার্নার্ট, "হ্যাকিশ এবং কৌতুকপূর্ণ" "পাইথন এফএইউ অফিশিয়াল অফিশিয়াল দ্বারা প্রদত্ত সমাধান হিসাবে সমাধান হওয়ার জন্য প্রয়োজনীয় নয়" arily রেফারেন্সের জন্য ধন্যবাদ।
ডন হ্যাচ

4
একই পরিবর্তনশীল নামটি পুনরায় ব্যবহার করা এই ধারণাটির সাথে অপরিচিত কারও কাছে অস্পষ্ট। উদাহরণস্বরূপ এটি ল্যাম্বডা এন = মি থাকলে আরও ভাল। হ্যাঁ, আপনাকে আপনার কলব্যাকের পরম পরিবর্তন করতে হবে, তবে লুপের বডিটি একইরকম থাকতে পারে বলে আমি মনে করি।
নিক

4
পুরোপুরি +1! এটি সমাধান হওয়া উচিত ... গ্রহণযোগ্য সমাধান এটির মতো প্রায় ভাল নয়। সর্বোপরি, আমরা এখানে ল্যাম্বদা ফাংশনগুলি ব্যবহার করার চেষ্টা করছি ... সবকিছুকে কেবল কোনও defঘোষণায় সরানো হয় না ।
255.tar.xz

6

পাইথন অবশ্যই রেফারেন্স ব্যবহার করে তবে এই প্রসঙ্গে এটি কোনও বিষয় নয়।

যখন আপনি কোনও ল্যাম্বডা (বা কোনও ফাংশন, যেহেতু এটি হুবহু একই আচরণ) সংজ্ঞায়িত করেন, এটি রানটাইমের আগে ল্যাম্বডা অভিব্যক্তিটিকে মূল্যায়ন করে না:

# defining that function is perfectly fine
def broken():
    print undefined_var

broken() # but calling it will raise a NameError

আপনার ল্যাম্বদা উদাহরণের চেয়েও বিস্ময়কর:

i = 'bar'
def foo():
    print i

foo() # bar

i = 'banana'

foo() # you would expect 'bar' here? well it prints 'banana'

সংক্ষেপে, গতিশীল ভাবেন: ব্যাখ্যার আগে কোনও কিছুই মূল্যায়ন করা হয় না, এজন্য আপনার কোড এম এর সর্বশেষ মান ব্যবহার করে।

ল্যাম্বডা এক্সিকিউশনে যখন এটি মিটার সন্ধান করে, এমটিকে শীর্ষতম স্থান থেকে নেওয়া হয়, যার অর্থ অন্যরা যেমন নির্দেশ করেছেন; আপনি অন্য সুযোগ যুক্ত করে এই সমস্যাটি সমাধান করতে পারেন:

def factory(x):
    return lambda: callback(x)

for m in ('do', 're', 'mi'):
    funcList.append(factory(m))

এখানে, ল্যাম্বডাকে যখন ডাকা হয়, তখন এটি একটি এক্স এর জন্য ল্যাম্বডা সংজ্ঞা স্কোপে দেখায়। এই এক্সটি কারখানার শরীরে সংজ্ঞায়িত স্থানীয় ভেরিয়েবল। এই কারণে, ল্যাম্বদা এক্সিকিউটে ব্যবহৃত মানটি সেই মান হবে যা কারখানায় কল করার সময় প্যারামিটার হিসাবে পাস হয়েছিল। আর দোরমি!

একটি নোট হিসাবে, আমি কারখানাকে ফ্যাক্টরি (এম) হিসাবে [এক্স দ্বারা প্রতিস্থাপন করুন] হিসাবে সংজ্ঞা দিতে পারতাম, আচরণটি একই রকম। আমি স্বচ্ছতার জন্য আলাদা নাম ব্যবহার করেছি :)

আপনি দেখতে পাবেন যে আন্দ্রেজ বাউর একই রকম ল্যাম্বডা সমস্যা পেয়েছে। এই ব্লগে আকর্ষণীয় হ'ল মন্তব্যগুলি, যেখানে আপনি অজগর বন্ধের বিষয়ে আরও জানতে পারবেন :)


1

ইস্যুটির সাথে সরাসরি সম্পর্কিত নয়, তবুও জ্ঞানের এক অমূল্য অংশ: ফ্রেডরিক লুন্ডের পাইথন অবজেক্টস


4
আপনার উত্তরের সাথে সরাসরি সম্পর্কিত নয়, তবে বিড়ালছানাগুলির জন্য অনুসন্ধান: google.com/search?q=kitten
এককভাবে

@ সিঙ্গলেটেড: ওপি যদি আমার নিবন্ধটি প্রবন্ধটি সরবরাহ করে তবে তারা প্রথমে প্রশ্ন জিজ্ঞাসা করবে না; এ কারণেই এটি পরোক্ষভাবে সম্পর্কিত। আমি নিশ্চিত যে বিড়ালছানাগুলি কীভাবে আমার উত্তরের সাথে পরোক্ষভাবে সম্পর্কিত তা
বোঝাতে

1

হ্যাঁ, এটি সুযোগের সমস্যা, এটি বাইরের মিটার সাথে আবদ্ধ হয়, আপনি ল্যাম্বডা ব্যবহার করছেন বা স্থানীয় ফাংশন। পরিবর্তে, একটি ফান্টার ব্যবহার করুন:

class Func1(object):
    def __init__(self, callback, message):
        self.callback = callback
        self.message = message
    def __call__(self):
        return self.callback(self.message)
funcList.append(Func1(callback, m))

1

ল্যাম্বডা থেকে সলিউটন বেশি ল্যাম্বডা

In [0]: funcs = [(lambda j: (lambda: j))(i) for i in ('do', 're', 'mi')]

In [1]: funcs
Out[1]: 
[<function __main__.<lambda>>,
 <function __main__.<lambda>>,
 <function __main__.<lambda>>]

In [2]: [f() for f in funcs]
Out[2]: ['do', 're', 'mi']

বাইরের lambdaবর্তমান মূল্য বাঁধে করতে ব্যবহৃত হয় iথেকে j

প্রতিটি সময় বাইরের lambdaএটা বলা হয় ভেতরের একটি দৃষ্টান্ত তোলে lambdaসঙ্গে jবর্তমান মূল্য আবদ্ধ iযেমন i'র মান


0

প্রথমত, আপনি যা দেখছেন তা কোনও সমস্যা নয়, এবং কল-বাই-রেফারেন্স বা বাই-মান সম্পর্কিত নয়।

আপনি যে ল্যাম্বদা সিনট্যাক্সটি সংজ্ঞায়িত করেছেন তার কোনও পরামিতি নেই এবং এর মতো, প্যারামিটারের সাথে আপনি যে সুযোগটি দেখছেন mতা ল্যাম্বদা ফাংশনের বাহ্যিক। এই কারণেই আপনি এই ফলাফলগুলি দেখছেন।

ল্যাম্বদা সিনট্যাক্স, আপনার উদাহরণে প্রয়োজনীয় নয় এবং আপনি বরং একটি সাধারণ ফাংশন কলটি ব্যবহার করবেন:

for m in ('do', 're', 'mi'):
    callback(m)

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

একটি পার্শ্ব নোট হিসাবে, পরামিতি পাসিং সম্পর্কিত। পাইথনের প্যারামিটারগুলি সর্বদা বস্তুর উল্লেখ to অ্যালেক্স মার্তেলিকে উদ্ধৃত করতে:

টার্মিনোলজির সমস্যাটি এই কারণে হতে পারে যে পাইথনটিতে একটি নামের মান একটি বস্তুর সাথে সম্পর্কিত। সুতরাং, আপনি সর্বদা মানটি পাস করেন (কোনও অন্তর্নিহিত অনুলিপিটি নেই) এবং সেই মানটি সর্বদা একটি উল্লেখ। [...] এখন আপনি যদি এর জন্য কোনও নাম মুদ্রণ করতে চান, যেমন "অবজেক্ট রেফারেন্স দ্বারা", "অনির্দিষ্ট মান দ্বারা", বা যাই হোক না কেন, আমার অতিথি হোন। যে ভাষাগুলিতে "ভেরিয়েবলগুলি বাক্সগুলি হয়" এমন ভাষায় যেখানে সাধারণত "ভেরিয়েবলগুলি পোস্ট হয় এটি" আইএমএইচও, আইএমএইচও তে আরও কার্যকরভাবে ব্যবহৃত হয় এমন পরিভাষাগুলি পুনরায় ব্যবহার করার চেষ্টা করা হয়েছে, সাহায্যের চেয়ে বিভ্রান্ত হওয়ার সম্ভাবনা বেশি।


0

চলকটি mক্যাপচার করা হচ্ছে, সুতরাং আপনার ল্যাম্বডা এক্সপ্রেশনটি সর্বদা এর "বর্তমান" মানটি দেখে।

আপনার যদি কোনও মুহুর্তে কার্যকরভাবে মানটি ক্যাপচার করতে হয়, তবে কোনও ফাংশন লিখুন প্যারামিটার হিসাবে আপনি যে মান চান তা গ্রহণ করে এবং ল্যাম্বডা এক্সপ্রেশনটি ফিরিয়ে দেয়। এই মুহুর্তে, ল্যাম্বদা প্যারামিটারের মানটি ক্যাপচার করবে , যা আপনি একাধিক বার ফাংশনটি কল করার সময় পরিবর্তন হবে না:

def callback(msg):
    print msg

def createCallback(msg):
    return lambda: callback(msg)

#creating a list of function handles with an iterator
funcList=[]
for m in ('do', 're', 'mi'):
    funcList.append(createCallback(m))
for f in funcList:
    f()

আউটপুট:

do
re
mi

0

পাইথনে ক্লাসিক অর্থে আসলে কোনও ভেরিয়েবল নেই, কেবলমাত্র নামগুলি প্রযোজ্য বস্তুর রেফারেন্স দ্বারা আবদ্ধ। এমনকি ফাংশনগুলি পাইথনে কিছু ধরণের অবজেক্ট এবং ল্যাম্বডাস বিধিটিকে ব্যতিক্রম করে না :)


যখন আপনি "ক্লাসিক অর্থে" বলেন, "আপনার অর্থ," সিটির মতো রয়েছে। " পাইথন সহ প্রচুর ভাষা সি এর চেয়ে আলাদাভাবে ভেরিয়েবল প্রয়োগ করে
নেড ব্যাচেল্ডার

0

পার্শ্ব দ্রষ্টব্য হিসাবে map, যদিও কিছু সুপরিচিত পাইথন চিত্র দ্বারা তুচ্ছ করা হয়েছে, এমন একটি নির্মাণকে বাধ্য করে যা এই ক্ষতিটিকে প্রতিরোধ করে।

fs = map (lambda i: lambda: callback (i), ['do', 're', 'mi'])

নোট: প্রথম lambda iউত্তরে অন্য উত্তরে কারখানার মতো কাজ করে।

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