Pergunta

recentemente para uma aula de programação, nos foi dada a missão de escrever um programa em qualquer linguagem que, dada n , irá produzir todos os possíveis desarranjos para um array p de tamanho n tal que p [i] = i para todo i: 0 <= i yield.

Exemplo: n = 3, [0, 1, 2] não é um desarranjo, mas [2, 0, 1] é bem como [1, 2, 0]

.

Eu vim com uma solução pseudocódigo que iria trabalhar, mas o problema foi que era necessário loops de energia (isto é, n loops aninhados onde n é apenas conhecida pelo tempo de execução). Para fazer isso, eu gerado n loops aninhados em código Ruby em uma corda, em seguida, eval-ed da cadeia. Minha solução trabalhou, no entanto o meu professor achou que o uso de algumas gotos teria sido melhor solução (mais fácil de ler, pelo menos) do que a geração de código dinâmico.

Eu estava sob a impressão de que goto sempre foi uma má escolha. Por que tempo de execução avaliação de código gerado dinamicamente ser uma escolha pior do que goto? O código gerado é limpo e simples, e parece bastante eficiente para o problema dado. A única entrada do usuário em que a geração de código depende é n , que é verificado para garantir que ele é um valor inteiro de antemão. Ele yields apenas desarranjos únicas, como deveria.

Eu não estou pedindo uma solução para o meu trabalho de programação, eu só quero saber razões por trás do uso de goto sobre avaliação dinâmica de código, ou vice-versa.

Editar:. para esclarecer, a atribuição incluída escrever um programa usando iteradores e outro usando recursão, de modo a versão iterativa não estava necessariamente concebido para ser eficiente

Foi útil?

Solução

Essa é uma pergunta muito interessante. - Eu não tenho certeza de que não há uma resposta definitiva

O problema com Goto é a sua utilização de forma não estruturada - um goto é um "salto aleatório maciça" assim no caso geral, depois do salto, você não sabe de onde veio que faz com que todos os tipos de questões tanto em termos de depuração e manutenção e - num sentido mais formal com provando "correção" do código. Claro que existem línguas (eu estive em torno de um tempo), onde você não tem uma opção em que ponto você impor uma estrutura no código. A linha inferior é que não é que GOTO é ruim tanto como a maneira que Goto é usado (e abusado) o que é mau e que faz sua uma construção perigoso ter disponível.

Usando a geração de código e, em seguida, avaliar o resultado é inteligente :) No entanto "inteligente" nem sempre é uma coisa boa e eu suspeito que, em parte, o problema com o uso que como uma solução é que não é realmente lidar com o problema como pretendido. Isso pode ser "batota" em um sentido - pelo menos tanto quanto o seu professor está em causa - não invalida a sua solução, mas pode torná-lo "deselegante". Os problemas de depuração e manutenção também surgem em relação ao código.

A solução recursiva - especialmente porque eu me lembro vagamente que está sendo ensinado (há 25 anos) que se pode recursão geralmente relaxar em loops - seria provavelmente a mais elegante

.

Definitivamente uma questão interessante!

Outras dicas

Ambos GOTO e geração de código são soluções deselegantes para este problema IMO. Há um algoritmo recursivo que é provavelmente o direito resposta aqui.

Sem ver seu código, eu tenderia a lado com o prof. Se é uma escolha entre GoTo e dinâmica de código, eu inclinar-se para o primeiro. GoTo não são sempre uma má escolha.

Você pode resolver quase todos os problemas sem o uso de GOTOS. Especificamente com loops que você pode usar o break e continue a implitly usar gotos enquanto padrão de código ainda são mantidas.

n loops aninhados soar como um mau plano, e eu sugiro que você, em vez olhar para uma série de funções recursivas. Toda vez que você precisa fazer uma recursão n-loops que você deve sempre estar pensando.

código dinâmico não é verificável tempo de compilação, o que significa que qualquer erro vai passar despercebido até tempo de execução. Potencialmente tornando-os mais difíceis de encontrar. Para rubi, isso significaria que os erros de sintaxe não ser encontrado pelo IDE ou editor, o que acontecer de você estar usando. Isso é uma vantagem para a escolha Goto.

Eu acho que eu teria que ver tanto para tomar uma decisão neste caso. Eu não vi o código, mas eu estou disposto a apostar há uma boa solução que não usa código dinâmico, ou GOTO. Goto nem sempre é ruim, mas se você está pensando em usá-lo você provavelmente não ter feito as melhores decisões de projeto até este ponto e, provavelmente, querer rever a sua solução.

Em um dos meus assignement na faculdade, uma vez eu tinha que fazer algo que era relativamente semelhante Minha solução foi usar uma função recursiva, passando a matriz, o tamanho da matriz eo nível de aninhamento como argumento. A função de seguida, chamar-se com aninhamento nível 1, até que o nível de assentamento é igual ao tamanho da matriz. No Goto, nenhuma avaliação de código, apenas o código limpo!

Exemplo

function computeDerangement(yourArray, loopLevel, arraySize)
{
    //We check to see if the loop level is the same as the array size
    //if true, then we have executed exactly n loop
    if (loopLevel == arraySize) {
         display(yourArray); //Display being some kind of function that show the array,
                             //you get the idea
    } else {
        while(something) {
            //Here you put the logic that you execute at one level of the loop

            //Then you call yourself with one more level of nesting
            computeDerangement(yourArray, loopLevel + 1, arraySize);
        }
    }
}

Espero que ajuda!

Eu tenho Goto nunca usei na minha vida, então eu tenho certeza que há sempre uma maneira de evitá-los

A principal razão as pessoas a evitar instruções goto é que eles podem fazer seu programa mais difícil de entender.

Sem ter visto o seu código, eu acho que é mais difícil de entender do que o programa equivalente usando Goto ...

GOTO Solution - um goto é conveniente quando simulando uma chamada de função. Aqui está uma solução não-recurisve que simplesmente simula a solução recursiva usando uma pilha e uma etiqueta Goto para voltar ao ponto em que a chamada de função teria ocorrido.

Consulte o procedimento recursiva (I colocaram isso como uma resposta em separado) para comparação.

Opção On Strict Option Explicit On

Module Module1 Dim x As Stack

Private Sub printGeneratedList(ByVal generatedList As List(Of Integer))
    For Each el In generatedList
        Console.Write(el & " ")
    Next
    Console.WriteLine()
End Sub
Private Sub generateAux(ByVal i As Integer, ByVal n As Integer, _
                        ByVal generatedList As List(Of Integer))
    Dim stackI As Stack(Of Integer) = New Stack(Of Integer)
    Dim stackJ As Stack(Of Integer) = New Stack(Of Integer)


    Dim j As Integer

StartLabel:

    j = 0

    If i >= n Then
        printGeneratedList(generatedList)
        If stackI.Count = 0 Then
            Return
        Else
            GoTo ReturnLabel
        End If
    End If

     While j < n
        If Not j = i Then
            If Not generatedList.Contains(j) Then
                generatedList.Add(j)
                stackI.Push(i)
                stackJ.Push(j)
                i = i + 1
                GoTo StartLabel

ReturnLabel:

                i = stackI.Pop()
                j = stackJ.Pop()
                generatedList.Remove(j)
            End If
        End If
        j = j + 1
    End While
    If stackI.Count = 0 Then
        Return
    Else
        GoTo ReturnLabel
    End If
End Sub

Private Sub generate(ByVal n As Integer)
    Console.WriteLine("Generating for n = " & n.ToString())
    Dim l As List(Of Integer) = New List(Of Integer)
    If n < 0 Then
        Throw New Exception("n must be >= 0")
    End If
    generateAux(0, n, l)
End Sub

Sub Main()
    generate(0)
    Console.ReadLine()
    generate(1)
    Console.ReadLine()
    generate(2)
    Console.ReadLine()
    generate(3)
    Console.ReadLine()
    generate(4)
    Console.ReadLine()
End Sub

Módulo End

Goto não estiverem limpos. mas se alto desempenho é necessária, você precisa quebrar algumas dessas regras de codificação às vezes ...

Se a velocidade é realmente uma questão importante, por exemplo, se você quiser escrever uma biblioteca ou código que você tem grande exigeance, você pode considerar o uso Goto. com certeza você tem que prestar atenção que tudo corra bem.

Comment : No final, a CPU execução não faz nada mais do que simples saltos. Assim apenas que a linguagem de programação / o compilador cria-los. uso com cautela e não mexer com ele.

Tanto a solução de empreendedores e idéias dinâmicas de geração de código são ruins. Isso é facilmente resolvido com uma solução recursiva como mencionado por outros. Você simplesmente recursivamente gerar todas as permutações (solução recursiva padrão) e quando a geração é completo (ou seja, a folha da recursão) simplesmente ignorar retornando permutações que não são desarranjos.

solução recursiva - aqui está uma solução com poda precoce. Minha única pergunta é sobre enumerações: ele queria que você crie um enumerador que em cada chamada bem-sucedida iria gerar o próximo item na lista? Isso provavelmente seria mais fácil implementado através da criação de uma versão lambda deste programa -. Eu costumava fazer isso em lisp o tempo todo ao escrever geradores de consulta que gerariam a próxima resposta a uma pergunta ao fazer consultas estilo prólogo-intérprete

Opção On Strict Option Explicit On

Module Module1

Private Sub printGeneratedList(ByVal generatedList As List(Of Integer))
    For Each el In generatedList
        Console.Write(el & " ")
    Next
    Console.WriteLine()
End Sub

Private Sub generateAux(ByVal i As Integer, ByVal n As Integer, _
                    ByVal generatedList As List(Of Integer))
    If i >= n Then
        printGeneratedList(generatedList)
        Return
    End If
    For j As Integer = 0 To n - 1
        If Not j = i Then
            If Not generatedList.Contains(j) Then
                generatedList.Add(j)
                generateAux(i + 1, n, generatedList)
                generatedList.Remove(j)
            End If
        End If
    Next
End Sub

Private Sub generate(ByVal n As Integer)
    Console.WriteLine("Generating for n = " & n.ToString())
    Dim l As List(Of Integer) = New List(Of Integer)
    If n < 0 Then
        Throw New Exception("n must be >= 0")
    End If
    generateAux(0, n, l)
End Sub

Sub Main()
     generate(0)

    Console.ReadLine()
    generate(1)

    Console.ReadLine()
    generate(2)

    Console.ReadLine()
    generate(3)

    Console.ReadLine()
    generate(4)

    Console.ReadLine()

End Sub

Módulo End

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top