Индексирование с помощью указателей - как указатель работает со срезами? - PullRequest
0 голосов
/ 30 мая 2020

В приведенном ниже коде main():

package main

import (
    "fmt"

    "github.com/myhub/cs61a/poetry"
)

func main() {

    p := poetry.NewPoem([][]string{
        {
            "And from my pillow, looking forth by light",
            "Of moon or favoring stars, I could behold",
            "The antechapel where the statue stood",
            "Of Newton, with his prism and silent face,",
            "The marble index of a mind forever",
            "Voyaging through strange seas of thought, alone.",
        },
        {
            "inducted into Greek and Christian",
            "modes of thinking, must take a longer way around.",
            "Like children born into love, they exult in the here",
            "who have felt separation and grave misgivings, they",
            "and of humanity, and of God. Great literature, like",
            "struggles to reconcile suffering with faith in the ",
        },
    })

    fmt.Printf("%T\n", p[0])

}

p[0] отлично работает, указывая на первую строфу с помощью конструктора функции ниже:

package poetry

type Line string
type Stanza []Line
type Poem []Stanza

func NewPoem(s [][]string) Poem {
    var poem Poem
    for _, stanza := range s {
        var newStanza Stanza
        for _, line := range stanza {
            newStanza = append(newStanza, Line(line))
        }
        poem = append(poem, newStanza)
    }

    return poem
}

Если NewPoem() возвращает значение типа *Poem, как показано ниже:

package poetry

type Line string
type Stanza []Line
type Poem []Stanza

func NewPoem(s [][]string) *Poem {
    var poem Poem
    for _, stanza := range s {
        var newStanza Stanza
        for _, line := range stanza {
            newStanza = append(newStanza, Line(line))
        }
        poem = append(poem, newStanza)
    }

    return &poem
}

, то p[0] in main() дает ошибку ниже:

     Invalid operation: p[0] (type *poetry.Poem does not support indexing)

Почему указатель на фрагмент строки не поддерживает синтаксис p[0]?

Ответы [ 3 ]

6 голосов
/ 30 мая 2020

Почему указатель на фрагмент фрагмента строки не поддерживает синтаксис p[0]?

Короткий ответ: потому что язык spe c не позволяет этого.

Более длинный ответ: потому что указатели на срезы используются редко и зачем усложнять язык чем-то, что не приносит многим пользы? Если в редком случае вам это нужно, просто разыменуйте указатель и проиндексируйте срез. массивы чаще и полезнее (чем указатели на срезы). Подробнее об этом здесь: Нарезка указателя среза, переданного в качестве аргумента

2 голосов
/ 30 мая 2020

Почему указатель на фрагмент фрагмента строки не поддерживает синтаксис p [0]?

Потому что указатель на фрагмент (чего угодно) не является типом, определяемым языком для поддержки индексации. Вот SPE c:

Первичное выражение формы a[x] обозначает элемент массива, указатель на массив, срез, строку или отображение, индексированный x. Значение x называется индексом или ключом карты соответственно. Применяются следующие правила:

Источник: https://golang.org/ref/spec#Index_expressions

Он продолжается и явно определяет, как работает индексирование для каждого из поддерживаемых типов, и заканчивается говоря, что В противном случае [x] является незаконным.

Итак, в основном вы пытаетесь сделать что-то, что язык не позволяет .


Что касается того, почему, и говоря немного о вашем дизайне, указатели на фрагменты редко имеют смысл .

Во-первых, когда вы передаете фрагмент, объем перемещаемых данных абсолютно минимален (это просто заголовок фрагмента, который обычно составляет дюжину байтов), поэтому нет видимого выигрыша по сравнению с передачей указателя.

Затем в ваших комментариях в вопросе, который вы говорите , «выглядит как лучшая абстракция для предоставления указателя на тип Poem.» Это действительно зависит от того, что вы хотите делать с типом Poem. С учетом тех немногих деталей, которые вы указали, я бы сказал, что на самом деле передача *Poem, а не просто Poem, - худший вариант дизайна, поскольку вы ничего не получаете с указателем и фактически усложняете ситуацию. Например, вы не можете использовать индексацию (p[0]) ...


В комментарии к этому автоответчику вы спросили: видите ли вы [][]string{...} переход к NewPoem() хороший подход? .

Как я уже упоминал в комментарии, у нас не так много деталей, поэтому трудно сказать. Но в настоящее время вы можете сделать это, чтобы создать Poem:

p := poetry.Poem{
    {
        "And from my pillow, looking forth by light",
        "Of moon or favoring stars, I could behold",
        "The antechapel where the statue stood",
        "Of Newton, with his prism and silent face,",
        "The marble index of a mind forever",
        "Voyaging through strange seas of thought, alone.",
    },
    {
        "inducted into Greek and Christian",
        "modes of thinking, must take a longer way around.",
        "Like children born into love, they exult in the here",
        "who have felt separation and grave misgivings, they",
        "and of humanity, and of God. Great literature, like",
        "struggles to reconcile suffering with faith in the ",
    },
}

fmt.Printf("%T\n", p[0])

Нет необходимости в циклах или добавлении, или функции NewPoem или чего-либо еще для создания стихотворения непосредственно из строк. Просто используйте тип Poem{...} напрямую!

Если вам нужно сделать что-то другое, например, создать стихотворение путем чтения данных, скажем, из файла, вы можете вместо этого создать такую ​​функцию:

package poetry

func ReadFrom(rd io.Reader) Poem { ... }

Но для простого создания Поэмы непосредственно в коде нет необходимости усложнять вещи.

0 голосов
/ 30 мая 2020

Почему указатель на фрагмент фрагмента строки не поддерживает синтаксис p [0]?

Поскольку это указатель, он не имеет внутри индексируемых элементов, таких как массив или фрагмент. Если вы используете sh индексный доступ к Stanza, вы можете разыменовать указатель (таким образом получив срез), а затем получить доступ к 0 -ому элементу в срезе:

fmt.Printf("%T\n", (*p)[0])

Вывод:

main.Stanza

Или целый код примера: https://play.golang.org/p/7abmjp6AxA4

НО. Вам действительно нужен указатель на срез? Срезы в Golang - это очень легкие структуры - они состоят из 3-х 64-битных значений: указателя на массив с данными, текущей длины массива и максимальной длины массива - так называемой емкости среза.

Мы можем сравнить передачу среза с передачей на него указателя:

  1. 24 байта, и вы получите адрес, по которому находятся данные

  2. 8 байтов но затем вы должны разыменовать его - go в другое место в ОЗУ, чтобы получить срез (24 байта), а затем получить адрес данных. Такое косвенное обращение может оказаться более затратной операцией.

Пропуска среза хватает в 99% случаев. Даже когда вы читаете данные из файла методами ОС, вы передаете срез в качестве буфера.

f, _ := os.Open("/tmp/dat")

// Create a buffer of size 5
b1 := make([]byte, 5)

// Read some bytes from the beginning of the file. 
// Allow up to 5 to be read - this value is taken from slice length 
// Read returns how many bytes actually were read.
n, _ := f.Read(b1)

Подробнее: https://gobyexample.com/reading-files

...