[] इंटरफ़ेस [} स्ट्रिंग को [] स्ट्रिंग में नहीं बदल सकते


96

मैं कुछ कोड लिख रहा हूं, और मुझे तर्कों को पकड़ने और उनके माध्यम से पारित करने की आवश्यकता है fmt.Println
(मुझे इसका डिफ़ॉल्ट व्यवहार चाहिए, रिक्त स्थान द्वारा अलग किए गए तर्कों को लिखने और एक नई पंक्ति के बाद)। हालांकि यह लेता है []interface {}लेकिन flag.Args()रिटर्न एक []string
यहाँ कोड उदाहरण है:

package main

import (
    "fmt"
    "flag"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Args()...)
}

यह निम्न त्रुटि देता है:

./example.go:10: cannot use args (type []string) as type []interface {} in function argument

क्या यह एक बग है? कोई सरणी नहीं fmt.Printlnलेनी चाहिए ? वैसे, मैंने भी ऐसा करने की कोशिश की है:

var args = []interface{}(flag.Args())

लेकिन मुझे निम्नलिखित त्रुटि मिलती है:

cannot convert flag.Args() (type []string) to type []interface {}

क्या इसको हल करने के लिए "गो" तरीका है?


1
मैं एक साधारण उदाहरण ( go run test.go some test flags) के साथ खिलवाड़ कर रहा था , और यह flags.Args()...सिर्फ काम करने के लिए बदल रहा था flag.Args()(आउटपुट है [some test flags], न्यूलाइन द्वारा पीछा किया जाता है; वास्तविक झंडे दर्ज करने के साथ काम करने के लिए भी लगता है)। यह समझने का नाटक नहीं किया जाएगा, और स्टीफन का जवाब वैसे भी अधिक जानकारीपूर्ण है :)
रॉकेटडोनक

जवाबों:


115

यह एक बग नहीं है। fmt.Println()एक []interface{}प्रकार की आवश्यकता है । इसका मतलब है, यह interface{}मूल्यों का एक टुकड़ा होना चाहिए और "कोई भी टुकड़ा" नहीं होना चाहिए । स्लाइस को परिवर्तित करने के लिए, आपको प्रत्येक तत्व को लूप और कॉपी करना होगा।

old := flag.Args()
new := make([]interface{}, len(old))
for i, v := range old {
    new[i] = v
}
fmt.Println(new...)

आप किसी भी स्लाइस का उपयोग नहीं कर सकते इसका कारण यह है कि एक के बीच रूपांतरण []stringऔर []interface{}स्मृति लेआउट को बदलने की आवश्यकता होती है और ओ (एन) समय में होता है। एक प्रकार को परिवर्तित करने के interface{}लिए O (1) समय की आवश्यकता होती है। यदि उन्होंने इसे लूप के लिए अनावश्यक बनाया है, तो कंपाइलर को इसे सम्मिलित करने की आवश्यकता होगी।


2
वैसे, मुझे यह लिंक गोलंग-नट्स में मिला: groups.google.com/d/topic/golang-nuts/Il-tO1xtAyE/discussion
cruizh

2
हां, प्रत्येक पुनरावृत्ति को O (1) समय की आवश्यकता होती है और लूप को O (n) समय की आवश्यकता होती है। वही तो मैंने कहा। फ़ंक्शन प्राप्त करने के लिए के रूप में []string, यह एक की उम्मीद है interface{}। एक interface{}एक से एक अलग स्मृति लेआउट है stringतो तथ्य यह है कि प्रत्येक तत्व की जरूरत है परिवर्तित किया समस्या है।
स्टीफन वेनबर्ग

1
@karlrh: नहीं, मान लीजिए कि Printlnफ़ंक्शन स्लाइस को संशोधित करता है, और कुछ तत्व सेट करता है (यह नहीं करता है, लेकिन मान लीजिए कि यह करता है)। फिर यह किसी भी interface{}स्लाइस में डाल सकता है , जिसे केवल stringएस होना चाहिए । क्या आप वास्तव में चाहते हैं कि जावा जेनेरिक वाइल्डकार्ड जैसा कुछ है Slice<? extends []interface{}>, लेकिन यह गो में मौजूद नहीं है।
newacct

4
परिशिष्ट जादुई है। यह भाषा द्वारा विशेष रूप से निर्मित और व्यवहारित है। अन्य उदाहरणों में शामिल हैं new(), len(), और copy()golang.org/ref/spec#Appending_and_copying_slices
स्टीफन वेनबर्ग

1
आज मैंने सीखा 'नया' एक आरक्षित खोजशब्द नहीं है! और न ही बना है!
श्रीधर

11

यदि यह केवल उन तारों का एक टुकड़ा है जिसे आप प्रिंट करना चाहते हैं, तो आप रूपांतरण से बच सकते हैं और शामिल होकर सटीक समान आउटपुट प्राप्त कर सकते हैं:

package main

import (
    "fmt"
    "flag"
    "strings"
)

func main() {
    flag.Parse()
    s := strings.Join(flag.Args(), " ")
    fmt.Println(s)
}

11

इस स्थिति में, एक प्रकार का रूपांतरण अनावश्यक है। बस flag.Args()मान को पास करें fmt.Println


सवाल:

[] इंटरफ़ेस [} स्ट्रिंग को [] स्ट्रिंग में नहीं बदल सकते

मैं कुछ कोड लिख रहा हूं, और मुझे तर्कों को पकड़ने और उन्हें fmt.Println के माध्यम से पारित करने की आवश्यकता है (मुझे इसका डिफ़ॉल्ट व्यवहार चाहिए, रिक्त स्थान द्वारा अलग किए गए तर्क लिखने और एक नई पंक्ति के बाद)।

यहाँ कोड उदाहरण है:

package main

import (
    "fmt"
    "flag"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Args()...)
}

पैकेज ध्वज

import "flag"

दुर्गंध आर्ग

func Args() []string

Args गैर-ध्वज आदेश-पंक्ति तर्क लौटाता है।


पैकेज fmt

import "fmt"

कवक Println

func Println(a ...interface{}) (n int, err error)

Printlnअपने ऑपरेंड के लिए डिफ़ॉल्ट स्वरूपों का उपयोग करते हुए प्रारूप और मानक आउटपुट के लिए लिखते हैं। ऑपरेशंस के बीच स्पेस को हमेशा जोड़ा जाता है और एक नई रेखा को जोड़ा जाता है। यह लिखी गई बाइट्स की संख्या और किसी भी लेखन त्रुटि का सामना करता है।


इस स्थिति में, एक प्रकार का रूपांतरण अनावश्यक है। बस flag.Args()मान को पास करें fmt.Println, जो मूल्य के प्रकार के रूप में व्याख्या करने के लिए प्रतिबिंब का उपयोग करता है []string। पैकेज reflectरन-टाइम रिफ्लेक्शन, एक प्रोग्राम को मनमाने प्रकार के साथ ऑब्जेक्ट्स में हेरफेर करने की अनुमति देता है। उदाहरण के लिए,

args.go:

package main

import (
    "flag"
    "fmt"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Args())
}

आउटपुट:

$ go build args.go
$ ./args arg0 arg1
[arg0 arg1]
$ 

अगर मैं कोष्ठक को छोड़ना चाहता था, तो क्या रूपांतरण अभी भी अनावश्यक होगा?
क्रुज

1

Go में, कोई फ़ंक्शन केवल फ़ंक्शन परिभाषा में पैरामीटर सूची में निर्दिष्ट प्रकारों के तर्कों को स्वीकार कर सकता है। वैरिएड पैरामीटर भाषा सुविधा थोड़ी जटिल है, लेकिन यह अच्छी तरह से परिभाषित नियमों का पालन करती है।

इसके लिए फ़ंक्शन हस्ताक्षर fmt.Printlnहै:

func Println(a ...interface{}) (n int, err error)

प्रति भाषा specifiction ,

एक फ़ंक्शन हस्ताक्षर में अंतिम आने वाले पैरामीटर में एक प्रकार का उपसर्ग हो सकता है .... ऐसे पैरामीटर के साथ एक फ़ंक्शन को वैरेडिक कहा जाता है और उस पैरामीटर के लिए शून्य या अधिक तर्कों के साथ आमंत्रित किया जा सकता है।

इसका मतलब है कि आप प्रकार Printlnके तर्कों की एक सूची पास कर सकते हैं interface{}। चूंकि सभी प्रकार खाली इंटरफ़ेस को लागू करते हैं, आप किसी भी प्रकार के तर्कों की एक सूची पास कर सकते हैं, जो कि आप कैसे कॉल कर सकते हैं Println(1, "one", true), उदाहरण के लिए, त्रुटि के बिना। भाषा विनिर्देश के "पास होने वाले तर्क ... पैरामीटर" अनुभाग देखें :

पास किया गया मान एक नया स्लाइस प्रकार है [] T एक नए अंतर्निहित सरणी के साथ जिसका क्रमिक तत्व वास्तविक तर्क हैं, जिन्हें सभी को T के लिए असाइन किया जाना चाहिए।

उस हिस्से को जो आपको परेशान कर रहा है, ठीक उसके बाद विनिर्देश में है:

यदि अंतिम तर्क एक स्लाइस प्रकार [] T के लिए लागू होता है, तो इसे मान के रूप में अपरिवर्तित किया जा सकता है ... T पैरामीटर यदि तर्क का अनुसरण किया जाता है .... तो इस मामले में कोई नया स्लाइस नहीं बनता है।

flag.Args()प्रकार है []string। चूंकि Tमें Printlnहै interface{}, []Tहै []interface{}। तो सवाल नीचे आता है कि क्या एक स्ट्रिंग स्लाइस मान इंटरफ़ेस स्लाइस प्रकार के एक चर के लिए उपलब्ध है। आप एक उदाहरण के लिए, उदाहरण के लिए, अपने गो कोड में आसानी से परीक्षण कर सकते हैं:

s := []string{}
var i []interface{}
i = s

यदि आप ऐसे असाइनमेंट का प्रयास करते हैं, तो कंपाइलर इस त्रुटि संदेश को आउटपुट करेगा:

cannot use s (type []string) as type []interface {} in assignment

और यही कारण है कि आप एक तर्क के रूप में स्ट्रिंग स्लाइस के बाद दीर्घवृत्त का उपयोग नहीं कर सकते हैं fmt.Println। यह बग नहीं है, यह इरादा के अनुसार काम कर रहा है।

कई तरीकों से मुद्रित कर सकते हैं की अभी भी बहुत सारे हैं flag.Args()के साथ Printlnइस तरह के रूप में,

fmt.Println(flag.Args())

(जो fmt पैकेज डॉक्युमेंटेशन के[elem0 elem1 ...] अनुसार आउटपुट होगा )

या

fmt.Println(strings.Join(flag.Args(), ` `)

(जो स्ट्रिंग स्लाइस तत्वों को आउटपुट करेगा, प्रत्येक को एक स्थान से अलग किया जाएगा) स्ट्रिंग स्ट्रिंगर के साथ स्ट्रिंग पैकेज में सम्मिलित फ़ंक्शन का उपयोग करके , उदाहरण के लिए।


0

मुझे लगता है कि प्रतिबिंब का उपयोग करना संभव है, लेकिन मुझे नहीं पता कि क्या यह एक अच्छा समाधान है

package main

import (
    "fmt"
    "reflect"
    "strings"
)

type User struct {
    Name string
    Age  byte
}

func main() {
    flag.Parse()
    fmt.Println(String(flag.Args()))
    fmt.Println(String([]string{"hello", "world"}))
    fmt.Println(String([]int{1, 2, 3, 4, 5, 6}))
    u1, u2 := User{Name: "John", Age: 30},
        User{Name: "Not John", Age: 20}
    fmt.Println(String([]User{u1, u2}))
}

func String(v interface{}) string {
    val := reflect.ValueOf(v)
    if val.Kind() == reflect.Array || val.Kind() == reflect.Slice {
        l := val.Len()
        if l == 0 {
            return ""
        }
        if l == 1 {
            return fmt.Sprint(val.Index(0))
        }
        sb := strings.Builder{}
        sb.Grow(l * 4)
        sb.WriteString(fmt.Sprint(val.Index(0)))
        for i := 1; i < l; i++ {
            sb.WriteString(",")
            sb.WriteString(fmt.Sprint(val.Index(i)))
        }
        return sb.String()
    }

    return fmt.Sprintln(v)
}

आउटपुट:

$ go run .\main.go arg1 arg2
arg1,arg2
hello,world
1,2,3,4,5,6
{John 30},{Not John 20}

-1

fmt.Printlnपरिवर्तनशील पैरामीटर लेता है

func Println (a ... इंटरफ़ेस {}) (n int, इरेट एरर)

flag.Args()में परिवर्तित किए बिना मुद्रित करना संभव है[]interface{}

func main() {
    flag.Parse()
    fmt.Println(flag.Args())
}

2
fmt.Println6 वर्षों में हस्ताक्षर नहीं बदले हैं (और यह सिर्फ एक पैकेज परिवर्तन था error)। यहां तक ​​कि अगर यह था, कल्पना स्पष्ट रूप से कहता है `प्रकार के अंतिम पैरामीटर पी के साथ चर ... टी, तो एफ के भीतर पी प्रकार टाइप करने के लिए बराबर है [] टी।` तो यह कोई फर्क नहीं पड़ता। fmt.Println(flags.Args()...)अभी भी काम नहीं करता है (आप स्लाइस विस्तार याद कर रहे हैं), fmt.Println(flags.Args())हमेशा काम किया।
मार्क

आपको यह जानकारी कैसे मिली कि fmt.Println के हस्ताक्षर 6 वर्षों में नहीं बदले हैं? क्या आपने सोर्स कोड चेक किया?
शाहियार


सेशन पर टिप्पणी यह ​​सुझाव देती है कि उस समय वापस काम flag.Args()करने के लिए तर्क के रूप में पूरी तरह से उपयोग करना fmt.Println। (यह उस समय नहीं हुआ तो आश्चर्य होगा)
पत्ती

इसलिए? यदि कोई टिप्पणी मौजूद है, तो इसका मतलब है कि मेरा जवाब मतदान होना चाहिए?
शाहियार
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.