पुनरावृति करते समय मान बदलें


153

मान लें कि मेरे पास ये प्रकार हैं:

type Attribute struct {
    Key, Val string
}
type Node struct {
    Attr []Attribute
}

और मैं उन्हें बदलने के लिए अपने नोड के गुणों पर पुनरावृति करना चाहता हूं।

मैं करने में सक्षम होने के लिए प्यार होता:

for _, attr := range n.Attr {
    if attr.Key == "href" {
        attr.Val = "something"
    }
}

लेकिन जैसा attrकि एक सूचक नहीं है, यह काम नहीं करेगा और मुझे करना होगा:

for i, attr := range n.Attr {
    if attr.Key == "href" {
        n.Attr[i].Val = "something"
    }
}

क्या कोई सरल या तेज तरीका है? क्या सीधे संकेत प्राप्त करना संभव है range?

जाहिर है कि मैं सिर्फ पुनरावृत्ति के लिए संरचनाओं को बदलना नहीं चाहता हूं और अधिक क्रिया समाधान कोई समाधान नहीं हैं।


2
तो आप Array.prototype.forEachजावास्क्रिप्ट में किसी तरह चाहते हैं ?
फ्लोरियन मार्गाइन

यह एक दिलचस्प विचार है और यह एक समाधान हो सकता है लेकिन एक फ़ंक्शन को कॉल करना जो प्रत्येक पुनरावृत्ति पर एक फ़ंक्शन को कॉल करेगा एक सर्वर साइड भाषा में भारी और गलत दिखता है। और जेनेरिक की कमी इस अनुभव को और भी भारी बना देती है।
डेनसिटी सेग्रुट

ईमानदारी से, मुझे नहीं लगता कि यह इतना भारी है। एक फ़ंक्शन या दो को कॉल करना बहुत सस्ता है, यह आमतौर पर वह है जो कंपाइलर सबसे अधिक अनुकूलन करते हैं। मैं यह कोशिश करूँगा और यह देखने के लिए बेंचमार्क करूंगा कि क्या यह बिल फिट बैठता है।
फ्लोरियन मार्गाइन

जैसा कि गो में जेनेरिक की कमी है, मुझे डर है कि पारित होने वाला फ़ंक्शन forEachआवश्यक रूप से एक प्रकार के दावे के साथ शुरू होगा। यह वास्तव में से बेहतर नहीं है attr := &n.Attr[i]
डेनसिटी सेग्रुट

जवाबों:


152

नहीं, जो संक्षिप्त नाम आप चाहते हैं वह संभव नहीं है।

इसका कारण यह है कि rangeउन स्लाइस से मानों को कॉपी करता है जिन्हें आप अधिक देख रहे हैं। सीमा के बारे में विनिर्देश का कहना है:

Range expression                          1st value             2nd value (if 2nd variable is present)
array or slice  a   [n]E, *[n]E, or []E   index    i  int       a[i]       E

इसलिए, सीमा a[i]सरणियों / स्लाइस के लिए अपने दूसरे मूल्य के रूप में उपयोग करती है , जिसका प्रभावी अर्थ है कि मूल्य की प्रतिलिपि बनाई गई है, जिससे मूल मूल्य अछूत हो गया है।

इस व्यवहार को निम्न कोड द्वारा प्रदर्शित किया जाता है :

x := make([]int, 3)

x[0], x[1], x[2] = 1, 2, 3

for i, val := range x {
    println(&x[i], "vs.", &val)
}

कोड आपको रेंज से मूल्य और स्लाइस में वास्तविक मूल्य के लिए पूरी तरह से अलग-अलग मेमोरी स्थान प्रिंट करता है:

0xf84000f010 vs. 0x7f095ed0bf68
0xf84000f014 vs. 0x7f095ed0bf68
0xf84000f018 vs. 0x7f095ed0bf68

तो केवल एक चीज आप कर सकते हैं या तो पॉइंटर्स या इंडेक्स का उपयोग करें, जैसा कि पहले ही jnml और peterSO द्वारा प्रस्तावित है।


16
इस पर विचार करने का एक तरीका यह है कि मान प्रदान करने के लिए एक प्रतिलिपि बनाई जाती है। यदि आपने वैल देखा: = x [1], तो यह पूरी तरह से आश्चर्यजनक होगा कि वैल x [1] की एक प्रति थी। सीमा के बारे में कुछ विशेष करने के बारे में सोचने के बजाय, याद रखें कि एक सीमा का प्रत्येक पुनरावृत्ति सूचकांक और मूल्य चर को निर्दिष्ट करने से शुरू होता है, और यह है कि उस सीमा के बजाय असाइनमेंट है जो प्रतिलिपि का कारण बनता है।
एंडी डेविस

क्षमा करें, मैं अभी भी यहाँ थोड़ा भ्रमित हूँ। यदि लूप के लिए दूसरा मान एक [i] है, तो a[i]लूप के लिए और a[i]जैसा हम लिखते हैं , उससे अलग क्या है ? यह एक ही चीज़ की तरह दिखता है, लेकिन यह सही नहीं है?
तिएन गुयेन

1
@ Ti @nNguyễnHoàng इसकी दूसरी वापसी मूल्य के रूप में rangeदेता है a[i]। यह ऑपरेशन, val = a[i]जैसा कि द्वारा किया जाता rangeहै मूल्य की एक प्रति बनाता है इसलिए किसी भी लेखन ऑपरेशन को valकॉपी पर लागू किया जाता है।
नीमो

37

आप इसके बराबर कुछ माँग रहे हैं:

package main

import "fmt"

type Attribute struct {
    Key, Val string
}
type Node struct {
    Attr []Attribute
}

func main() {

    n := Node{
        []Attribute{
            {"key", "value"},
            {"href", "http://www.google.com"},
        },
    }
    fmt.Println(n)

    for i := 0; i < len(n.Attr); i++ {
        attr := &n.Attr[i]
        if attr.Key == "href" {
            attr.Val = "something"
        }
    }

    fmt.Println(n)
}

आउटपुट:

{[{key value} {href http://www.google.com}]}
{[{key value} {href something}]}

यह Attributeस्लाइस बाउंड चेक की कीमत पर, संभवतः - बड़े - प्रकार के मूल्यों की प्रतिलिपि बनाने से बचा जाता है । आपके उदाहरण में, प्रकार Attributeअपेक्षाकृत छोटा है, दो stringस्लाइस संदर्भ: 64-बिट आर्किटेक्चर मशीन पर 2 * 3 * 8 = 48 बाइट्स।

आप बस यह भी लिख सकते हैं:

for i := 0; i < len(n.Attr); i++ {
    if n.Attr[i].Key == "href" {
        n.Attr[i].Val = "something"
    }
}

लेकिन, एक rangeखंड के साथ एक समान परिणाम प्राप्त करने का तरीका , जो एक प्रति बनाता है, लेकिन स्लाइस सीमा जांच को कम करता है, यह है:

for i, attr := range n.Attr {
    if attr.Key == "href" {
        n.Attr[i].Val = "something"
    }
}

2
यह एक दया है कि value := &someMap[key]होगा काम नहीं करता है, तो someMapएक हैmap
warvariuc

अपने पहले कोड स्निपेट में peterSO क्या आपको इसके लिए कुछ असाइन करने के लिए अट्रेक्शन डिफरेंस नहीं करना है? अर्थात्*attr.Val = "something"
होमम बहारानी

25

मैं आपके अंतिम सुझाव को अनुकूलित करूंगा और रेंज के केवल-इंडेक्स संस्करण का उपयोग करूंगा।

for i := range n.Attr {
    if n.Attr[i].Key == "href" {
        n.Attr[i].Val = "something"
    }
}

यह मेरे लिए सरल है n.Attr[i]कि परीक्षण Keyऔर लाइन दोनों को स्पष्ट रूप से संदर्भित करता है जो एक और दूसरे के लिए Valउपयोग करने के बजाय सेट करता है ।attrn.Attr[i]


15

उदाहरण के लिए:

package main

import "fmt"

type Attribute struct {
        Key, Val string
}

type Node struct {
        Attr []*Attribute
}

func main() {
        n := Node{[]*Attribute{
                &Attribute{"foo", ""},
                &Attribute{"href", ""},
                &Attribute{"bar", ""},
        }}

        for _, attr := range n.Attr {
                if attr.Key == "href" {
                        attr.Val = "something"
                }
        }

        for _, v := range n.Attr {
                fmt.Printf("%#v\n", *v)
        }
}

खेल का मैदान


उत्पादन

main.Attribute{Key:"foo", Val:""}
main.Attribute{Key:"href", Val:"something"}
main.Attribute{Key:"bar", Val:""}

वैकल्पिक दृष्टिकोण:

package main

import "fmt"

type Attribute struct {
        Key, Val string
}

type Node struct {
        Attr []Attribute
}

func main() {
        n := Node{[]Attribute{
            {"foo", ""},
            {"href", ""},
            {"bar", ""},
        }}

        for i := range n.Attr {
                attr := &n.Attr[i]
                if attr.Key == "href" {
                        attr.Val = "something"
                }
        }

        for _, v := range n.Attr {
                fmt.Printf("%#v\n", v)
        }
}

खेल का मैदान


आउटपुट:

main.Attribute{Key:"foo", Val:""}
main.Attribute{Key:"href", Val:"something"}
main.Attribute{Key:"bar", Val:""}

मुझे लगा कि यह स्पष्ट है लेकिन मैं उन संरचनाओं को नहीं बदलना चाहता जो मुझे मिलती हैं (वे go.net/htmlपैकेज से हैं)
डेनिस सेगुरेट

1
@ डायस्ट्रो: ऊपर दिया गया दूसरा तरीका ओपी को टाइप ("स्ट्रक्चर्स") नहीं बदलता है।
zzzz

हां, मुझे पता है, लेकिन यह वास्तव में कुछ भी नहीं ला रहा है। मैं एक विचार की उम्मीद कर रहा था कि मैं शायद याद कर सकता हूं। मैं आपको विश्वास दिलाता हूं कि कोई सरल उपाय नहीं है तो इसका उत्तर होगा।
डेनसिटी सेगुरेट

1
@dystroy: यह करता है कुछ लाने के लिए, यह नहीं है यहां कॉपी और पूरे गुण वापस। और हां, मुझे पूरा विश्वास है कि तत्व की दोहरी प्रतिलिपि (r + w) अद्यतन से बचने के लिए एक स्लाइस तत्व का पता लेना सबसे उपयुक्त उपाय है।
zzzz
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.