1

ANSWERED! I posted an answer with code that I modified from Arbitur. Thanks to Phillip as well for the key advice that lead me to redirect my searches.

TLDR:

is there a way to run a function from a string? Like RunFunction("myfunction_" + number + "()"), or, is a way to hardcode like myfunction_\&number() ?

I'm Justin and this is my first post. I've worked hard the past 2 months to not ask a question, but I'm finally breaking bad. I'll try to be brief but I'm terribly wordy (and this long intro doesn't help). Appreciation in advance for your patience.

Basically, I'm making an engine that transforms simple words and phrases into Swift code; the goal of this is to have something to quickly make Text games, educational apps, and anything involving a non-linear set of questions / answers that change based on how you respond.

Now, I'm working on what I thought would be an easy part: storing the actual text for the Labels and Buttons (I'm working in Xcode / iOS atm). I decided to model it like film-makers / novelists do: a movie is composed of Scenes which are composed of Clips. You can also think of this as Chapters / Paragraphs

So (not actual code):

App = (Scene 1 -> Scene 2 -> Scene 3, etc)
Scene 1 = (Clip 1 -> Clip 2 -> Clip 3, etc)
Scene 2 =  Clip 2 -> Clip 5 -> Clip 1, based on responses                        

....And so on.

The way I think (and program), I tend to avoid Classes as much as possible, outside of being strictly data or strictly function structures.

So, my first solution was to create a struct (or class) with nested functions:

struct SceneList {

    //fScene calls, and passes the clip# from the button click
    func fPlayClip(clipNUM: Int)
    {
        //Do logic stuff, then play the correct clip
    }

    //Called from button clicks on main screen; passes in clip#
    func fScene1(play_clip clipNumber: Int){

        //sub functions(S1)
        func fClip1(){ print("I'm scene 1, clip 1")}
        func fClip2(){ print ("I'm scene 1, clip 2")}

        fPlayClip(clipNumber)
    }

    func fScene2(play_clip clipNumber: Int){

        //sub functions(S2)
        func fClip1(){ print("Yo, scene 2, clip 1")}
        func fClip2(){ print ("Yo, scene 2, clip 2")}

        fPlayClip(clipNumber)
    }
}

Unfortunately, this design failed because there is no way for me to call sub-functions from fPlayClip(), so I took another approach:

  //////////////////////////
 /////SceneList.swift////// 
//////////////////////////  

struct Scene1{
    func fClip1(){ print("I'm scene 1, clip 1")}
    func fClip2(){ print ("I'm scene 1, clip 2")}}

struct Scene2{
    func fClip1(){ print("Yo, scene 2, clip 1")}
    func fClip2(){ print ("Yo, scene 2, clip 2")}}



  //////////////////////////
 ////////Main.swift//////// 
//////////////////////////  

// Implemention of the structs / clips.
// (playScene is called in button click method.)
func playScene(/*button prams go here*/){

    // Switch(){ case desired:          // Solve for X,
        var oScenePlayer = SceneX()     // where X is the desired scene

    // Switch(){ case desired:          // Solve for Z,
        oScenePlayer.fClipZ()           // where Z is the desired clip
}

Again, this failed because I couldn't use just one object, [oScenePlayer], because each struct was a different type.

I then went around for a while trying to figure out a way to destroy the object, then recreate it, but I couldn't do that. Then, I looked around for a way to reassign one object to another class type, but couldn't. Then, I peered into the extensions, protocols, generic types, etc, trying to figure out a way to extend / inherent to get this to work, but failed.

So now, my final two /working/ solutions, are to just create a new object on scene changes, and wait for ARC or whatever to destroy the old one; or, for me to go back to the first example, and simply embed [fPlayScene] into every function:

//SOLUTION 1:
struct SceneList {

    func fScene1(play_clip clipNumber: Int){

        //sub functions(S1)
        func fClip1(){ print("I'm scene 1, clip 1")}
        func fClip2(){ print ("I'm scene 1, clip 2")}

        //Do logic stuff below, solving for Y
        case desired:
        fClipY()
    }
}
//SOLUTION 2:  
  //////////////////////////
 /////SceneList.swift////// 
//////////////////////////  
struct Scene1{

    func fClip1(){ print("I'm scene 1, clip 1")}
    func fClip2(){ print ("I'm scene 1, clip 2")}}
}

//And so on...


  //////////////////////////
 ////////Main.swift//////// 
//////////////////////////  

//////////////////////////
// Inside the globalish area:
let oScene1: Scene1,
    oScene2: Scene2
    //And so on...

var global_next_scene = 1
var global_next_clip  = 1


/////////////////////////////////
// Inside the button call method:
func playScene(next_scene: Int, next_clip: Int){


    switch(next_scene){                 //Find the scene
        case 1:
            oScene1 = Scene1()

            switch(next_clip){          //Play the clip
                case 1:
                    oScene1.fClip1()
    }
}

Basically, I feel that I'm using WAY too many switch statements, and in way too many places, (there could be hundreds of scenes and thousands of clips), when something as simple as a RunFunction("fClip" + next_clip + "()") would work, but I don't know of any 'execute a swift command from a string' functions :[, or something where I could hardcode like fClip\&next_clip\() which I think was possible in c++ 15 years ago when I last programmed anything

I came up with a few other crazy ideas to implement this, I even thought about OOP (shudder) embedded classes and having member variables hold instances of the subclasses, but with my current knowledge / resources I can't find a simpler way to do this than the last 2 snippets.

All of the structs, member functions, switch statements, etc, are going to be auto-generated via my engine--so it's not that I'm looking for a faster way to do this, it just seems like an inefficient / taxing way to do it.

Any guidance would be appreciated. Thanks much in advance, and I'm surprised it's taken me this long to need to ask a question here ^-^()

Peace and blessings

Fluidity
  • 3,985
  • 1
  • 13
  • 34

2 Answers2

2

It seems to me that you're mixing what is naturally data into your code. That is, you're creating functions that know which element they're responsible for...and that's what leads to all the duplication you're wondering about.

Perhaps there are parts of the problem I don't understand, but why not have a structure such as:

class Clip {
    func play() {
        print("Playing \(self)")
    }
}

class Scene {
    var clips = [Clip]()
    func play() {
        print("Playing \(self)")
        for aClip in clips {
            aClip.play()
        }
    }
    func playClip(number: Int) {
        if number >= 0 && number < clips.count {
            clips[number].play()
        }
    }
}

class SceneList {
    var scenes = [Scene]()
    func play() {
        print("Playing \(self)")
        for aScene in scenes {
            aScene.play()
        }
    }
    func playScene(number: Int) {
        if number >= 0 && number < scenes.count {
            scenes[number].play()
        }
    }
}

var list = SceneList()
for _ in 0..<2 {
    var scene = Scene()
    list.scenes.append(scene)
    for _ in 0..<3 {
        scene.clips.append(Clip())
    }
}
list.play()

Add whatever other properties and logic you like but the main point is to turn those numbered functions into generic functions with data references.

Phillip Mills
  • 30,888
  • 4
  • 42
  • 57
  • Thanks! This is similar to another thing that I had considered, such as having a string.xml. I'm not quite sure how I would implement this though, because each series of clips (say, 1-10 belong to scene 1), and scene 1 can play any clip in any order, depending on user choices. I would then have to have my engine override / spit out a bunch of classes / objects to a .txt, and I'm not sure how to put that together. The array approach may very well be the best one. – Fluidity Jul 15 '16 at 14:08
  • As long as you can turn a user choice into a number, you can tell the scene to `playClip(choice)`. – Phillip Mills Jul 15 '16 at 14:11
  • I'll have to play around with this. It certainly gives me some ideas. I was toying about saving objects and this seems like a good option. xD – Fluidity Jul 15 '16 at 14:16
  • While you're thinking about it.... :) What I've posted uses arrays because your samples had numeric references to the scenes and clips. Since you talk about words and phrases, though, it may turn out to be more convenient to use dictionaries and implement methods such as `func playClip(name: String)`. – Phillip Mills Jul 15 '16 at 14:19
  • I usually always use arrays for everything, but in my "editor" where I write the program, the numbers / series are already known ahead of time, the only thing *not known* is what choice is going to be displayed. I was thinking about doing dictionaries, or multi-argument switch statements, here is a pic of what the "editor" looks like https://www.dropbox.com/s/m06aboltceqc661/Screenshot%202016-07-15%2010.28.41.png?dl=0 That then prints to the log or .txt/swift actual swift code that handles the logic and updates the screen – Fluidity Jul 15 '16 at 14:29
1

Phillip's answer inspired me to look more into arrays and separating data from functions, and indeed what I was looking for was available closures--I didn't know that you could call a closure simply from stating (unwrapping) a variable :) What a neat language!

This works perfectly, and now I don't need any classes or structs ^-^() (all the better!!)

//initializer
var clips_for_scene = [
    1: { print("initialized") }
]

//SceneList
var scenes = [
    1: {  clips_for_scene = [
        1: { print("scene 1, clip 1") },
        2: { print("s1, c2") }]},
    2: { clips_for_scene = [
        1: { print("scene 2, clip 1") },
        2: { print("s2, c2") }]}]


func playClip(which_scene: Int, _ which_clip:Int){

    scenes[which_scene]?()
    clips_for_scene[which_clip]?()
}


playClip(1, 1)
playClip(1, 2)
playClip(2, 1)
playClip(2, 2)

//output:
//scene 1, clip 1
//s1, c2
//scene 2, clip 1
//s2, c2

Thanks to Arbitur as well, as their answer on a separate question: https://stackoverflow.com/a/30286375/6593818

Community
  • 1
  • 1
Fluidity
  • 3,985
  • 1
  • 13
  • 34