Asosiy kontentga o‘tish
Innovialabs

Qiymat va havola bo‘yicha uzatish: argumentlar qanday ishlaydi

Asoslar4 daq

Qisqacha

Ko‘pchilik mashhur tillar qiymat nusxasini uzatadi, obyektlar uchun esa bu qiymat — havola: funksiya umumiy obyektni o‘zgartira oladi, lekin chaqiruvchi o‘zgaruvchisini qayta tayinlay olmaydi; haqiqiy havola bo‘yicha uzatish faqat til buni aniq ruxsat bergan joyda bor, masalan C# dagi ref yoki Go dagi ko‘rsatkichlar.

Qisqa javob

Uchta modelga duch kelasiz:

  • Qiymat bo‘yicha uzatish — funksiya nusxa oladi. Uni o‘zgartirish chaqiruvchi kodga ta’sir qilmaydi.
  • Havola bo‘yicha uzatish — funksiya chaqiruvchi o‘zgaruvchisining taxallusini oladi. Parametrga qiymat berish shu o‘zgaruvchini o‘zgartiradi.
  • Birgalikda foydalanish bo‘yicha uzatish (call by sharing, «havola qiymat bo‘yicha uzatiladi») — funksiya o‘sha obyektga havola nusxasini oladi. U obyektni o‘zgartira oladi, lekin parametrni qayta tayinlash faqat lokal nusxani o‘zgartiradi.

Python, JavaScript va Java obyektlar uchun uchinchi modeldan foydalanadi. Shuning uchun «Java havola bo‘yicha uzatadi» degan mashhur gap noto‘g‘ri.

Tillar bo‘yicha

TilStandart holatHaqiqiy havola bo‘yicha uzatish
PythonBirgalikda foydalanish (hammasi obyekt)Yo‘q
JavaScriptPrimitivlar uchun qiymat, obyektlar uchun birgalikda foydalanishYo‘q
JavaPrimitivlar uchun qiymat, obyektlar uchun birgalikda foydalanishYo‘q
GoDoim qiymat; slice va map ichki ko‘rsatkichlarga egaKo‘rsatkichlar (*T)
C#struct uchun qiymat, class uchun birgalikda foydalanishref, out, in

Python va JavaScript

def update(items):
    items.append(4)   # umumiy ro‘yxatni o‘zgartiradi: chaqiruvchi ko‘radi
    items = [0]       # lokal nomni qayta bog‘laydi: tashqarida ko‘rinmaydi

data = [1, 2, 3]
update(data)
print(data)  # [1, 2, 3, 4]

JavaScript obyekt va massivlar bilan xuddi shunday ishlaydi. Satrlar va sonlar o‘zgarmas, shuning uchun ikkala tilda ham funksiya ularni joyida o‘zgartira olmaydi.

Java

Metod havolaning nusxasini oladi. list.add(x) chaqiruvchining ro‘yxatini o‘zgartiradi, list = new ArrayList<>() esa yo‘q. Java metodini chaqiruvchi o‘zgaruvchisini almashtirishga majburlab bo‘lmaydi.

Go

Go hamma narsani, jumladan strukturalarni ham nusxalaydi. Funksiya strukturani o‘zgartira olishi uchun ko‘rsatkich uzating:

func rename(u *User) { u.Name = "Ann" }

Slice va map — umumiy ma’lumotlarga ishora qiluvchi kichik sarlavhalar. Funksiya ichida s[0] ni o‘zgartirish tashqarida ko‘rinadi, lekin append yangi massiv ajratishi mumkin va slice qaytarilmasa, bu o‘zgarish ko‘rinmaydi.

C#

Strukturalar nusxalanadi, klass nusxalari esa umumiy. ref kalit so‘zi haqiqiy havola bo‘yicha uzatishni beradi: ref parametrga qiymat berish chaqiruvchi o‘zgaruvchisini o‘zgartiradi. out qo‘shimcha qiymatlarni qaytarish uchun, in esa faqat o‘qish uchun havola uzatadi va katta strukturalarni nusxalashdan qutqaradi.

Argumentni o‘zgartirish bilan bog‘liq odatiy xatolar

  • Kutilmagan yon ta’sirlar. Yordamchi funksiya elementlarni o‘chirib, ro‘yxatni «tozalaydi» va boshqa joyda kerak bo‘lgan asl ma’lumotlar ham o‘zgarib ketadi.
  • Python dagi o‘zgaruvchan standart qiymatlar. Standart obyekt bir marta yaratiladi va barcha chaqiruvlar uchun umumiy bo‘ladi:
def add_tag(tag, tags=[]):   # xato: barcha chaqiruvlar uchun bitta ro‘yxat
    tags.append(tag)
    return tags

def add_tag(tag, tags=None):  # tuzatish
    tags = [] if tags is None else tags
    tags.append(tag)
    return tags
  • UI dagi umumiy holat. React va shunga o‘xshash kutubxonalarda holatni joyida o‘zgartirish havolani o‘zgartirmaydi va yangilanish sezilmasligi mumkin.
  • Go dagi append kutilmagan holatlari. Ikki slice bitta massivni bo‘lishishi va bir-birining ma’lumotlarini qayta yozishi mumkin.

Nusxalash: sayoz va chuqur

Mustaqil obyekt kerak bo‘lsa, uni nusxalang — lekin nusxa qanchalik chuqur ketishini tushuning.

  • Sayoz nusxa (shallow copy) yangi tashqi konteyner yaratadi, ichki obyektlar esa umumiy qoladi.
  • Chuqur nusxa (deep copy) hamma narsani rekursiv nusxalaydi, umumiy hech narsa qolmaydi.
TilSayoz nusxaChuqur nusxa
Pythonlist(x), x.copy(), copy.copy(x)copy.deepcopy(x)
JavaScript[...arr], {...obj}, Object.assignstructuredClone(obj)
Javanew ArrayList<>(list), nusxa konstruktorlariQo‘lda yoki serializatsiya orqali
Gocopy(dst, src), maps.CloneQo‘lda
C#MemberwiseClone, new List<T>(list)Qo‘lda yoki serializatsiya orqali

Lug‘atlar ro‘yxatining sayoz nusxasi lug‘atlarning o‘zini baribir bo‘lishadi. «Nusxaladim-ku, lekin baribir o‘zgardi» holatining eng ko‘p uchraydigan sababi shu.

Xatolardan qanday qochish kerak

  • Argumentlarni o‘zgartirmang, agar bu funksiya nomidan aniq bo‘lmasa (sort_in_place, appendTo).
  • Kirishlarni o‘zgartirish o‘rniga yangi qiymat qaytaring — bunday kodni test qilish osonroq.
  • Til imkon bergan joyda o‘zgarmas ma’lumotlarni afzal ko‘ring: kortejlar, frozenset, readonly, records, sayoz himoya uchun Object.freeze.
  • Chegarada nusxalang — ma’lumotlar modulga kirganda yoki saqlanganda ularni bir marta nusxalang.
  • Chuqur nusxani ongli ravishda qiling. U sekinroq va tashqi resurslarga ega obyektlarda buzilishi mumkin, shuning uchun uni faqat to‘liq mustaqillik haqiqatan kerak joyda qo‘llang.

Ko‘p beriladigan savollar

Yo‘q. Java doim qiymat bo‘yicha uzatadi. Obyektlar uchun qiymat — havola, shuning uchun metod obyekt maydonlarini o‘zgartira oladi, lekin chaqiruvchi o‘zgaruvchisini boshqa obyektga ishora qildira olmaydi.

Bunda yordam beramiz

O‘zingiz shug‘ullanmasdan natija olishni istasangiz — biz har kuni shu bilan shug‘ullanamiz.

Loyihani muhokama qilish

Yana o‘qing

G‘oya yoki vazifangiz bormi? Bizga aytib bering

Ish kuni davomida javob beramiz va yechim taklif qilamiz. Konsultatsiya bepul.